first commit

This commit is contained in:
armansansd
2023-07-12 16:31:19 +01:00
commit 3424b1927b
23306 changed files with 2217776 additions and 0 deletions
Generated Vendored Executable
+20
View File
@@ -0,0 +1,20 @@
Copyright (c) 2013 Ben Newman <bn@cs.stanford.edu>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+379
View File
@@ -0,0 +1,379 @@
Commoner [![Build Status](https://travis-ci.org/benjamn/commoner.png?branch=master)](https://travis-ci.org/benjamn/commoner)
---
Commoner makes it easy to write scripts that flexibly and efficiently
transpile any dialect of JavaScript into a directory structure of
Node-compatible CommonJS module files.
This task is made possible by
1. a declarative syntax for defining how module source code should be
found and processed,
2. the use of [promises](https://github.com/kriskowal/q) to manage an
asynchronous build pipeline, and
3. never rebuilding modules that have already been built.
The output files can be required seamlessly by Node, or served by any
static file server, or bundled together using a tool such as
[Browserify](https://github.com/substack/node-browserify),
[WrapUp](https://github.com/kamicane/wrapup), or
[Stitch](https://github.com/sstephenson/stitch) for delivery to a web
browser.
If you pass the `--relativize` option, Commoner also takes care to rewrite
all `require` calls to use [relative module
identifiers](http://wiki.commonjs.org/wiki/Modules/1.1#Module_Identifiers),
so that the output files can be installed into any subdirectory of a
larger project, and external tools do not have to give special treatment
to top-level modules (or even know which modules are top-level and which
are nested).
Commoner was derived from an earlier, more opinionated tool called
[Brigade](https://github.com/benjamn/brigade) that provided additional
support for packaging modules together into multiple non-overlapping
bundles. Commoner grew out of the realization that many tools already
exist for bundling CommonJS modules, but that fewer tools focus on getting
to that point.
Installation
---
From NPM:
npm install commoner
From GitHub:
cd path/to/node_modules
git clone git://github.com/reactjs/commoner.git
cd commoner
npm install .
Usage
---
Here's the output of `bin/commonize --help`:
```
Usage: commonize [options] <source directory> <output directory> [<module ID> [<module ID> ...]]
Options:
-h, --help output usage information
-V, --version output the version number
-c, --config [file] JSON configuration file (no file means STDIN)
-w, --watch Continually rebuild
-x, --extension <js | coffee | ...> File extension to assume when resolving module identifiers
--relativize Rewrite all module identifiers to be relative
--follow-requires Scan modules for required dependencies
--cache-dir <directory> Alternate directory to use for disk cache
--no-cache-dir Disable the disk cache
--source-charset <utf8 | win1252 | ...> Charset of source (default: utf8)
--output-charset <utf8 | win1252 | ...> Charset of output (default: utf8)
```
In a single sentence: the `commonize` command finds modules with the given
module identifiers in the source directory and places a processed copy of
each module into the output directory, along with processed copies of all
required modules.
If you do not provide any module identifiers, `commonize` will process all
files that it can find under the source directory that have the preferred
file extension (`.js` by default). If your source files have a file
extension other than `.js`, use the `-x` or `--extension` option to
specify it. For example, `--extension coffee` to find `.coffee` files.
Output
---
Commoner prints various status messages to `STDERR`, so that you can see
what it's doing, or figure out why it's not doing what you thought it
would do.
The only information it prints to `STDOUT` is a JSON array of module
identifiers, which includes the identifiers passed on the command line and
all their dependencies. This array contains no duplicates.
Internally, each module that Commoner generates has a hash computed from
the module's identifier, source code, and processing steps. Since this
hash can be computed before processing takes place, Commoner is able to
avoid processing a module if it has ever previously processed the same
module in the same way.
If you dig into [the
code](https://github.com/reactjs/commoner/blob/5e7f65cab2/lib/context.js#L94),
you'll find that Commoner maintains a cache directory (by default,
`~/.commoner/module-cache/`) containing files with names like
`9ffc5c853aac07bc106da1dc1b2486903ca688bf.js`. When Commoner is about to
process a module, it checks its hash against the file names in this
directory. If no match is found, processing procedes and the resulting
file is written to the cache directory with a new hash. If the appropriate
hash file is already present in the cache directory, however, Commoner
merely creates a hard link between the hash file and a file with a more
meaningful name in the output directory.
When you pass the `--watch` flag to `bin/commonize`, Commoner avoids
exiting after the first build and instead watches for changes to
previously read files, printing a new JSON array of module identifiers to
`STDOUT` each time rebuilding finishes. Thanks to the caching of processed
modules, the time taken to rebuild is roughly proportional to the number
of modified files.
Customization
---
The `bin/commonize` script is actually quite simple, and you can write
similar scripts yourself. Let's have a look:
```js
#!/usr/bin/env node
require("commoner").resolve(function(id) {
var context = this;
return context.getProvidedP().then(function(idToPath) {
// If a module declares its own identifier using @providesModule
// then that identifier will be a key in the idToPath object.
if (idToPath.hasOwnProperty(id))
return context.readFileP(idToPath[id]);
});
}, function(id) {
// Otherwise assume the identifier maps directly to a filesystem path.
// The readModuleP method simply appends the preferred file extension
// (usually .js) to the given module identifier and opens that file.
return this.readModuleP(id);
});
```
The scriptable interface of the `commoner` module abstracts away many of
the annoyances of writing a command-line script. In particular, you don't
have to do any parsing of command-line arguments, and you don't have to
worry about installing any dependencies other than `commoner` in your
`$NODE_PATH`.
What you are responsible for, at a minimum, is telling Commoner how to
find the source of a module given a module identifier, and you do this by
passing callback functions to `require("commoner").resolve`. The script
above uses two strategies that will be tried in sequence: first, it calls
the helper function `this.getProvidedP` to retrieve an object mapping
identifiers to file paths (more about this below); and, if that doesn't
work, it falls back to interpreting the identifier as a path relative to
the source directory.
Now, you might not care about `this.getProvidedP`. It's really just a
proof of concept that Commoner can support modules that declare their own
identifiers using the `// @providesModule <identifier>` syntax, and I
included it by default because it doesn't make a difference unless you
decide to use `@providesModule`. If you don't like it, you could write an
even simpler script:
```js
#!/usr/bin/env node
require("commoner").resolve(function(id) {
return this.readModuleP(id);
});
```
The point is, it's entirely up to you to define how module identifiers are
interpreted. In fact, the source you return doesn't even have to be valid
JavaScript. It could be [CoffeeScript](http://coffeescript.org/), or
[LESS](http://lesscss.org/), or whatever language you prefer to write by
hand. Commoner doesn't care what your source code looks like, because
Commoner allows you to define arbitrary build steps to turn that source
code into plain old CommonJS.
Let's consider the example of using LESS to write dynamic CSS
modules. First, let's apply what we already know to give special meaning
to `.less` files:
```js
#!/usr/bin/env node
require("commoner").resolve(function(id) {
if (isLess(id))
return this.readFileP(id);
}, function(id) {
return this.readModuleP(id);
});
function isLess(id) {
return /\.less$/i.test(id);
}
```
All this really accomplishes is to avoid appending the `.js` file
extension to identifiers that already have the `.less` extension.
Now we need to make sure the contents of `.less` files somehow get
transformed into plain old CommonJS, and for that we need
`require("commoner").process`:
```js
require("commoner").resolve(function(id) {
if (isLess(id))
return this.readFileP(id);
}, function(id) {
return this.readModuleP(id);
}).process(function(id, source) {
if (isLess(id))
return compileLessToJs(source);
return source;
});
```
How should `compileLessToJs` be implemented? At a high level, I propose
that we generate a CommonJS module that will append a new `<style>` tag to
the `<head>` the first time the module is required. This suggests to me
that we need to take the CSS generated by LESS and somehow embed it as a
string in a CommonJS module with a small amount of boilerplate JS.
Here's a first attempt:
```js
function compileLessToJs(less) {
var css = require("less").render(less);
return 'require("css").add(' + JSON.stringify(css) + ");";
}
```
Implementing a `css` module with an appropriate `add` method is an
exercise that I will leave to the reader (hint: you may find [this
StackOverflow answer](http://stackoverflow.com/a/524721/128454) useful).
This almost works, but there's one problem: `require("less").render` does
not actually return a string! For better or worse, it passes the compiled
CSS to a callback function, which would make our task extremely painful
*if Commoner were not deeply committed to supporting asynchronous
processing*.
Commoner uses promises for asynchronous control flow, so we need to return
a promise if we can't return a string immediately. The easiest way to make
a promise is to call `this.makePromise` in the following style:
```js
#!/usr/bin/env node
require("commoner").resolve(function(id) {
if (isLess(id))
return this.readFileP(id);
}, function(id) {
return this.readModuleP(id);
}).process(function(id, source) {
if (isLess(id)) {
return this.makePromise(function(nodeStyleCallback) {
compileLessToJs(source, nodeStyleCallback);
});
}
return source;
});
function compileLessToJs(less, callback) {
require("less").render(less, function(err, css) {
callback(err, 'require("css").add(' + JSON.stringify(css) + ");")
});
}
```
And we're done! This example was admittedly pretty involved, but if you
followed it to the end you now have all the knowledge you need to write
source files like `sidebar.less` and require them from other modules by
invoking `require("sidebar.less")`. (And, by the way, embedding dynamic
CSS modules in your JavaScript turns out to be an excellent idea.)
Generating multiple files from one source module
---
Commoner is not limited to generating just one output file from each
source module. For example, if you want to follow best practices for
producing source maps, you probably want to create a `.map.json` file
corresponding to every `.js` file that you compile.
Recall that normally your `.process` callback returns a string (or a
promise for a string) whose contents will be written as a `.js` file in
the output directory. To write more than one file, just return an object
whose keys are the file extensions of the files you want to write, and
whose values are either strings or promises for strings representing the
desired contents of those files.
Here's an example of generating two different files for every source
module, one called `<id>.map.json` and the other called `<id>.js`:
```js
require("commoner").resolve(function(id) {
return this.readModuleP(id);
}).process(function(id, source) {
var result = compile(source);
return {
".map.json": JSON.stringify(result.sourceMap),
".js": [
result.code,
"//# sourceMappingURL=" + id + ".map.json"
].join("\n")
};
});
```
Note that
```js
return {
".js": source
};
```
would be equivalent to
```js
return source;
```
so you only have to return an object when you want to generate multiple
files. However, the `.js` key is mandatory when returning an object.
For your convenience, if you have a sequence of multiple processing
functions, the values of the object returned from each step will be
resolved before the object is passed along to the next processing
function, so you can be sure all the values are strings (instead of
promises) at the beginning of the next processing function.
Configuration
---
Of course, not all customization requires modifying code. Most of the
time, in fact, configuration has more to do with providing different
dynamic values to the same code.
For that kind of configuration, you don't need to modify your Commoner
script at all, because Commoner scripts accept a flag called `--config`
that can either specify a JSON file or (if `--config` is given without a
file name) read a string of JSON from `STDIN`.
Examples:
bin/commonize source/ output/ main --config release.json
bin/commonize source/ output/ main --config debug.json
echo '{"debug":false}' | bin/commonize source/ output/ main --config
echo '{"debug":true}' | bin/commonize source/ output/ main --config /dev/stdin
This configuration object is exposed to the `.resolve` and `.process`
callbacks as `this.config`. So, for example, if you wanted to implement
minification as a processing step, you might do it like this:
```js
require("commoner").resolve(function(id) {
return this.readModule(id);
}).process(function(id, source) {
if (this.config.debug)
return source;
return minify(source);
});
```
Perhaps the coolest thing about the configuration object is that Commoner
generates a recursive hash of all its properties and their values which is
then incorporated into every module hash. This means that two modules with
the same identifier and identical source code and processing steps will
have distinct hashes if built using different configuration objects.
Custom Options
---
You can define custom options for your script by using the `option` function.
```js
require("commoner").resolve(function(id) {
return this.readModule(id);
}).option(
'--custom-option',
'This is a custom option.'
).process(function(id, source) {
if (this.options.customOption) {
source = doCustomThing(source);
}
return source;
});
```
For more information of the options object available inside the `process` function see [Commander](https://github.com/visionmedia/commander.js).
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env node
require("commoner").resolve(function(id) {
var context = this;
return context.getProvidedP().then(function(idToPath) {
// If a module declares its own identifier using @providesModule
// then that identifier will be a key in the idToPath object.
if (idToPath.hasOwnProperty(id))
return context.readFileP(idToPath[id]);
});
}, function(id) {
// Otherwise assume the identifier maps directly to a filesystem path.
return this.readModuleP(id);
}).process(function(id, source) {
// As a simple example of a processing step, make sure the file ends
// with exactly one newline character.
return source.replace(/\s+$/m, "\n");
});
+117
View File
@@ -0,0 +1,117 @@
var assert = require("assert");
var Q = require("q");
var fs = require("fs");
var path = require("path");
var util = require("./util");
var EventEmitter = require("events").EventEmitter;
var hasOwn = Object.prototype.hasOwnProperty;
/**
* ReadFileCache is an EventEmitter subclass that caches file contents in
* memory so that subsequent calls to readFileP return the same contents,
* regardless of any changes in the underlying file.
*/
function ReadFileCache(sourceDir, charset) {
assert.ok(this instanceof ReadFileCache);
assert.strictEqual(typeof sourceDir, "string");
this.charset = charset;
EventEmitter.call(this);
Object.defineProperties(this, {
sourceDir: { value: sourceDir },
sourceCache: { value: {} }
});
}
util.inherits(ReadFileCache, EventEmitter);
var RFCp = ReadFileCache.prototype;
/**
* Read a file from the cache if possible, else from disk.
*/
RFCp.readFileP = function(relativePath) {
var cache = this.sourceCache;
relativePath = path.normalize(relativePath);
return hasOwn.call(cache, relativePath)
? cache[relativePath]
: this.noCacheReadFileP(relativePath);
};
/**
* Read (or re-read) a file without using the cache.
*
* The new contents are stored in the cache for any future calls to
* readFileP.
*/
RFCp.noCacheReadFileP = function(relativePath) {
relativePath = path.normalize(relativePath);
var added = !hasOwn.call(this.sourceCache, relativePath);
var promise = this.sourceCache[relativePath] = util.readFileP(
path.join(this.sourceDir, relativePath), this.charset);
if (added) {
this.emit("added", relativePath);
}
return promise;
};
/**
* If you have reason to believe the contents of a file have changed, call
* this method to re-read the file and compare the new contents to the
* cached contents. If the new contents differ from the contents of the
* cache, the "changed" event will be emitted.
*/
RFCp.reportPossiblyChanged = function(relativePath) {
var self = this;
var cached = self.readFileP(relativePath);
var fresh = self.noCacheReadFileP(relativePath);
Q.spread([
cached.catch(orNull),
fresh.catch(orNull)
], function(oldData, newData) {
if (oldData !== newData) {
self.emit("changed", relativePath);
}
}).done();
};
/**
* Invoke the given callback for all files currently known to the
* ReadFileCache, and invoke it in the future when any new files become
* known to the cache.
*/
RFCp.subscribe = function(callback, context) {
for (var relativePath in this.sourceCache) {
if (hasOwn.call(this.sourceCache, relativePath)) {
callback.call(context || null, relativePath);
}
}
this.on("added", function(relativePath) {
callback.call(context || null, relativePath);
});
};
/**
* Avoid memory leaks by removing listeners and emptying the cache.
*/
RFCp.clear = function() {
this.removeAllListeners();
for (var relativePath in this.sourceCache) {
delete this.sourceCache[relativePath];
}
};
function orNull(err) {
return null;
}
exports.ReadFileCache = ReadFileCache;
+384
View File
@@ -0,0 +1,384 @@
var assert = require("assert");
var path = require("path");
var fs = require("fs");
var Q = require("q");
var iconv = require("iconv-lite");
var ReadFileCache = require("./cache").ReadFileCache;
var Watcher = require("./watcher").Watcher;
var contextModule = require("./context");
var BuildContext = contextModule.BuildContext;
var PreferredFileExtension = contextModule.PreferredFileExtension;
var ModuleReader = require("./reader").ModuleReader;
var output = require("./output");
var DirOutput = output.DirOutput;
var StdOutput = output.StdOutput;
var util = require("./util");
var log = util.log;
var Ap = Array.prototype;
var each = Ap.forEach;
// Better stack traces for promises.
Q.longStackSupport = true;
function Commoner() {
var self = this;
assert.ok(self instanceof Commoner);
Object.defineProperties(self, {
customVersion: { value: null, writable: true },
customOptions: { value: [] },
resolvers: { value: [] },
processors: { value: [] }
});
}
var Cp = Commoner.prototype;
Cp.version = function(version) {
this.customVersion = version;
return this; // For chaining.
};
// Add custom command line options
Cp.option = function() {
this.customOptions.push(Ap.slice.call(arguments));
return this; // For chaining.
};
// A resolver is a function that takes a module identifier and returns
// the unmodified source of the corresponding module, either as a string
// or as a promise for a string.
Cp.resolve = function() {
each.call(arguments, function(resolver) {
assert.strictEqual(typeof resolver, "function");
this.resolvers.push(resolver);
}, this);
return this; // For chaining.
};
// A processor is a function that takes a module identifier and a string
// representing the source of the module and returns a modified version of
// the source, either as a string or as a promise for a string.
Cp.process = function(processor) {
each.call(arguments, function(processor) {
assert.strictEqual(typeof processor, "function");
this.processors.push(processor);
}, this);
return this; // For chaining.
};
Cp.buildP = function(options, roots) {
var self = this;
var sourceDir = options.sourceDir;
var outputDir = options.outputDir;
var readFileCache = new ReadFileCache(sourceDir, options.sourceCharset);
var waiting = 0;
var output = outputDir
? new DirOutput(outputDir)
: new StdOutput;
if (self.watch) {
new Watcher(readFileCache).on("changed", function(file) {
log.err(file + " changed; rebuilding...", "yellow");
rebuild();
});
}
function outputModules(modules) {
// Note that output.outputModules comes pre-bound.
modules.forEach(output.outputModule);
return modules;
}
function finish(result) {
rebuild.ing = false;
if (waiting > 0) {
waiting = 0;
process.nextTick(rebuild);
}
return result;
}
function rebuild() {
if (rebuild.ing) {
waiting += 1;
return;
}
rebuild.ing = true;
var context = new BuildContext(options, readFileCache);
if (self.preferredFileExtension)
context.setPreferredFileExtension(
self.preferredFileExtension);
context.setCacheDirectory(self.cacheDir);
context.setIgnoreDependencies(self.ignoreDependencies);
context.setRelativize(self.relativize);
context.setUseProvidesModule(self.useProvidesModule);
return new ModuleReader(
context,
self.resolvers,
self.processors
).readMultiP(context.expandIdsOrGlobsP(roots))
.then(context.ignoreDependencies ? pass : collectDepsP)
.then(outputModules)
.then(outputDir ? printModuleIds : pass)
.then(finish, function(err) {
log.err(err.stack);
if (!self.watch) {
// If we're not building with --watch, throw the error
// so that cliBuildP can call process.exit(-1).
throw err;
}
finish();
});
}
return (
// If outputDir is falsy, we can't (and don't need to) mkdirP it.
outputDir ? util.mkdirP : Q
)(outputDir).then(rebuild);
};
function pass(modules) {
return modules;
}
function collectDepsP(rootModules) {
var modules = [];
var seenIds = {};
function traverse(module) {
if (seenIds.hasOwnProperty(module.id))
return Q(modules);
seenIds[module.id] = true;
return module.getRequiredP().then(function(reqs) {
return Q.all(reqs.map(traverse));
}).then(function() {
modules.push(module);
return modules;
});
}
return Q.all(rootModules.map(traverse)).then(
function() { return modules });
}
function printModuleIds(modules) {
log.out(JSON.stringify(modules.map(function(module) {
return module.id;
})));
return modules;
}
Cp.forceResolve = function(forceId, source) {
this.resolvers.unshift(function(id) {
if (id === forceId)
return source;
});
};
Cp.cliBuildP = function() {
var version = this.customVersion || require("../package.json").version;
return Q.spread([this, version], cliBuildP);
};
function cliBuildP(commoner, version) {
var options = require("commander");
var workingDir = process.cwd();
var sourceDir = workingDir;
var outputDir = null;
var roots;
options.version(version)
.usage("[options] <source directory> <output directory> [<module ID> [<module ID> ...]]")
.option("-c, --config [file]", "JSON configuration file (no file or - means STDIN)")
.option("-w, --watch", "Continually rebuild")
.option("-x, --extension <js | coffee | ...>",
"File extension to assume when resolving module identifiers")
.option("--relativize", "Rewrite all module identifiers to be relative")
.option("--follow-requires", "Scan modules for required dependencies")
.option("--use-provides-module", "Respect @providesModules pragma in files")
.option("--cache-dir <directory>", "Alternate directory to use for disk cache")
.option("--no-cache-dir", "Disable the disk cache")
.option("--source-charset <utf8 | win1252 | ...>",
"Charset of source (default: utf8)")
.option("--output-charset <utf8 | win1252 | ...>",
"Charset of output (default: utf8)");
commoner.customOptions.forEach(function(customOption) {
options.option.apply(options, customOption);
});
options.parse(process.argv.slice(0));
var pfe = new PreferredFileExtension(options.extension || "js");
// TODO Decide whether passing options to buildP via instance
// variables is preferable to passing them as arguments.
commoner.preferredFileExtension = pfe;
commoner.watch = options.watch;
commoner.ignoreDependencies = !options.followRequires;
commoner.relativize = options.relativize;
commoner.useProvidesModule = options.useProvidesModule;
commoner.sourceCharset = normalizeCharset(options.sourceCharset);
commoner.outputCharset = normalizeCharset(options.outputCharset);
function fileToId(file) {
file = absolutePath(workingDir, file);
assert.ok(fs.statSync(file).isFile(), file);
return pfe.trim(path.relative(sourceDir, file));
}
var args = options.args.slice(0);
var argc = args.length;
if (argc === 0) {
if (options.config === true) {
log.err("Cannot read --config from STDIN when reading " +
"source from STDIN");
process.exit(-1);
}
sourceDir = workingDir;
outputDir = null;
roots = ["<stdin>"];
commoner.forceResolve("<stdin>", util.readFromStdinP());
// Ignore dependencies because we wouldn't know how to find them.
commoner.ignoreDependencies = true;
} else {
var first = absolutePath(workingDir, args[0]);
var stats = fs.statSync(first);
if (argc === 1) {
var firstId = fileToId(first);
sourceDir = workingDir;
outputDir = null;
roots = [firstId];
commoner.forceResolve(
firstId,
util.readFileP(first, commoner.sourceCharset)
);
// Ignore dependencies because we wouldn't know how to find them.
commoner.ignoreDependencies = true;
} else if (stats.isDirectory(first)) {
sourceDir = first;
outputDir = absolutePath(workingDir, args[1]);
roots = args.slice(2);
if (roots.length === 0)
roots.push(commoner.preferredFileExtension.glob());
} else {
options.help();
process.exit(-1);
}
}
commoner.cacheDir = null;
if (options.cacheDir === false) {
// Received the --no-cache-dir option, so disable the disk cache.
} else if (typeof options.cacheDir === "string") {
commoner.cacheDir = absolutePath(workingDir, options.cacheDir);
} else if (outputDir) {
// The default cache directory lives inside the output directory.
commoner.cacheDir = path.join(outputDir, ".module-cache");
}
var promise = getConfigP(
workingDir,
options.config
).then(function(config) {
var cleanOptions = {};
options.options.forEach(function(option) {
var name = util.camelize(option.name());
if (options.hasOwnProperty(name)) {
cleanOptions[name] = options[name];
}
});
cleanOptions.version = version;
cleanOptions.config = config;
cleanOptions.sourceDir = sourceDir;
cleanOptions.outputDir = outputDir;
cleanOptions.sourceCharset = commoner.sourceCharset;
cleanOptions.outputCharset = commoner.outputCharset;
return commoner.buildP(cleanOptions, roots);
});
if (!commoner.watch) {
// If we're building from the command line without --watch, any
// build errors should immediately terminate the process with a
// non-zero error code.
promise = promise.catch(function(err) {
log.err(err.stack);
process.exit(-1);
});
}
return promise;
}
function normalizeCharset(charset) {
charset = charset
&& charset.replace(/[- ]/g, "").toLowerCase()
|| "utf8";
assert.ok(
iconv.encodingExists(charset),
"Unrecognized charset: " + charset
);
return charset;
}
function absolutePath(workingDir, pathToJoin) {
if (pathToJoin) {
workingDir = path.normalize(workingDir);
pathToJoin = path.normalize(pathToJoin);
// TODO: use path.isAbsolute when Node < 0.10 is unsupported
if (path.resolve(pathToJoin) !== pathToJoin) {
pathToJoin = path.join(workingDir, pathToJoin);
}
}
return pathToJoin;
}
function getConfigP(workingDir, configFile) {
if (typeof configFile === "undefined")
return Q({}); // Empty config.
if (configFile === true || // --config is present but has no argument
configFile === "<stdin>" ||
configFile === "-" ||
configFile === path.sep + path.join("dev", "stdin")) {
return util.readJsonFromStdinP(
1000, // Time limit in milliseconds before warning displayed.
"Expecting configuration from STDIN (pass --config <file> " +
"if stuck here)...",
"yellow"
);
}
return util.readJsonFileP(absolutePath(workingDir, configFile));
}
exports.Commoner = Commoner;
+265
View File
@@ -0,0 +1,265 @@
var assert = require("assert");
var path = require("path");
var Q = require("q");
var util = require("./util");
var spawn = require("child_process").spawn;
var ReadFileCache = require("./cache").ReadFileCache;
var grepP = require("./grep");
var glob = require("glob");
var env = process.env;
function BuildContext(options, readFileCache) {
var self = this;
assert.ok(self instanceof BuildContext);
assert.ok(readFileCache instanceof ReadFileCache);
if (options) {
assert.strictEqual(typeof options, "object");
} else {
options = {};
}
Object.freeze(options);
Object.defineProperties(self, {
readFileCache: { value: readFileCache },
config: { value: options.config },
options: { value: options },
optionsHash: { value: util.deepHash(options) }
});
}
var BCp = BuildContext.prototype;
BCp.makePromise = function(callback, context) {
return util.makePromise(callback, context);
};
BCp.spawnP = function(command, args, kwargs) {
args = args || [];
kwargs = kwargs || {};
var deferred = Q.defer();
var outs = [];
var errs = [];
var options = {
stdio: "pipe",
env: env
};
if (kwargs.cwd) {
options.cwd = kwargs.cwd;
}
var child = spawn(command, args, options);
child.stdout.on("data", function(data) {
outs.push(data);
});
child.stderr.on("data", function(data) {
errs.push(data);
});
child.on("close", function(code) {
if (errs.length > 0 || code !== 0) {
var err = {
code: code,
text: errs.join("")
};
}
deferred.resolve([err, outs.join("")]);
});
var stdin = kwargs && kwargs.stdin;
if (stdin) {
child.stdin.end(stdin);
}
return deferred.promise;
};
BCp.setIgnoreDependencies = function(value) {
Object.defineProperty(this, "ignoreDependencies", {
value: !!value
});
};
// This default can be overridden by individual BuildContext instances.
BCp.setIgnoreDependencies(false);
BCp.setRelativize = function(value) {
Object.defineProperty(this, "relativize", {
value: !!value
});
};
// This default can be overridden by individual BuildContext instances.
BCp.setRelativize(false);
BCp.setUseProvidesModule = function(value) {
Object.defineProperty(this, "useProvidesModule", {
value: !!value
});
};
// This default can be overridden by individual BuildContext instances.
BCp.setUseProvidesModule(false);
BCp.setCacheDirectory = function(dir) {
if (!dir) {
// Disable the cache directory.
} else {
assert.strictEqual(typeof dir, "string");
}
Object.defineProperty(this, "cacheDir", {
value: dir || null
});
};
// This default can be overridden by individual BuildContext instances.
BCp.setCacheDirectory(null);
function PreferredFileExtension(ext) {
assert.strictEqual(typeof ext, "string");
assert.ok(this instanceof PreferredFileExtension);
Object.defineProperty(this, "extension", {
value: ext.toLowerCase()
});
}
var PFEp = PreferredFileExtension.prototype;
PFEp.check = function(file) {
return file.split(".").pop().toLowerCase() === this.extension;
};
PFEp.trim = function(file) {
if (this.check(file)) {
var len = file.length;
var extLen = 1 + this.extension.length;
file = file.slice(0, len - extLen);
}
return file;
};
PFEp.glob = function() {
return "**/*." + this.extension;
};
exports.PreferredFileExtension = PreferredFileExtension;
BCp.setPreferredFileExtension = function(pfe) {
assert.ok(pfe instanceof PreferredFileExtension);
Object.defineProperty(this, "preferredFileExtension", { value: pfe });
};
BCp.setPreferredFileExtension(new PreferredFileExtension("js"));
BCp.expandIdsOrGlobsP = function(idsOrGlobs) {
var context = this;
return Q.all(
idsOrGlobs.map(this.expandSingleIdOrGlobP, this)
).then(function(listOfListsOfIDs) {
var result = [];
var seen = {};
util.flatten(listOfListsOfIDs).forEach(function(id) {
if (!seen.hasOwnProperty(id)) {
seen[id] = true;
if (util.isValidModuleId(id))
result.push(id);
}
});
return result;
});
};
BCp.expandSingleIdOrGlobP = function(idOrGlob) {
var context = this;
return util.makePromise(function(callback) {
// If idOrGlob already looks like an acceptable identifier, don't
// try to expand it.
if (util.isValidModuleId(idOrGlob)) {
callback(null, [idOrGlob]);
return;
}
glob(idOrGlob, {
cwd: context.readFileCache.sourceDir
}, function(err, files) {
if (err) {
callback(err);
} else {
callback(null, files.filter(function(file) {
return !context.isHiddenFile(file);
}).map(function(file) {
return context.preferredFileExtension.trim(file);
}));
}
});
});
};
BCp.readModuleP = function(id) {
return this.readFileCache.readFileP(
id + "." + this.preferredFileExtension.extension
);
};
BCp.readFileP = function(file) {
return this.readFileCache.readFileP(file);
};
// Text editors such as VIM and Emacs often create temporary swap files
// that should be ignored.
var hiddenExp = /^\.|~$/;
BCp.isHiddenFile = function(file) {
return hiddenExp.test(path.basename(file));
};
BCp.getProvidedP = util.cachedMethod(function() {
var context = this;
var pattern = "@providesModule\\s+\\S+";
return grepP(
pattern,
context.readFileCache.sourceDir
).then(function(pathToMatch) {
var idToPath = {};
Object.keys(pathToMatch).sort().forEach(function(path) {
if (context.isHiddenFile(path))
return;
var id = pathToMatch[path].split(/\s+/).pop();
// If we're about to overwrite an existing module identifier,
// make sure the corresponding path ends with the preferred
// file extension. This allows @providesModule directives in
// .coffee files, for example, but prevents .js~ temporary
// files from taking precedence over actual .js files.
if (!idToPath.hasOwnProperty(id) ||
context.preferredFileExtension.check(path))
idToPath[id] = path;
});
return idToPath;
});
});
var providesExp = /@providesModule[ ]+(\S+)/;
BCp.getProvidedId = function(source) {
var match = providesExp.exec(source);
return match && match[1];
};
exports.BuildContext = BuildContext;
+49
View File
@@ -0,0 +1,49 @@
var assert = require("assert");
var path = require("path");
var Q = require("q");
var fs = require("graceful-fs");
var util = require("./util");
var readdir = Q.denodeify(fs.readdir);
var lstat = Q.denodeify(fs.lstat);
function processDirP(pattern, dir) {
return readdir(dir).then(function(files) {
return Q.all(files.map(function(file) {
file = path.join(dir, file);
return lstat(file).then(function(stat) {
return stat.isDirectory()
? processDirP(pattern, file)
: processFileP(pattern, file);
});
})).then(function(results) {
return util.flatten(results);
});
});
}
function processFileP(pattern, file) {
return util.readFileP(file).then(function(contents) {
var result = new RegExp(pattern, 'g').exec(contents);
return result ? [{
path: file,
match: result[0]
}] : [];
});
}
module.exports = function(pattern, sourceDir) {
assert.strictEqual(typeof pattern, "string");
return processDirP(pattern, sourceDir).then(function(results) {
var pathToMatch = {};
results.forEach(function(result) {
pathToMatch[path.relative(
sourceDir,
result.path
).split("\\").join("/")] = result.match;
});
return pathToMatch;
});
};
+58
View File
@@ -0,0 +1,58 @@
var assert = require("assert");
var util = require("./util");
var log = util.log;
function AbstractOutput() {
assert.ok(this instanceof AbstractOutput);
Object.defineProperties(this, {
outputModule: { value: this.outputModule.bind(this) }
});
}
var AOp = AbstractOutput.prototype;
exports.AbstractOutput = AbstractOutput;
AOp.outputModule = function(module) {
throw new Error("not implemented");
};
function StdOutput() {
assert.ok(this instanceof StdOutput);
AbstractOutput.call(this);
}
var SOp = util.inherits(StdOutput, AbstractOutput);
exports.StdOutput = StdOutput;
SOp.outputModule = function(module) {
log.out(module.source);
};
function DirOutput(outputDir) {
assert.ok(this instanceof DirOutput);
assert.strictEqual(typeof outputDir, "string");
AbstractOutput.call(this);
Object.defineProperties(this, {
outputDir: { value: outputDir }
});
}
var DOp = util.inherits(DirOutput, AbstractOutput);
exports.DirOutput = DirOutput;
DOp.outputModule = function(module) {
return module.writeVersionP(this.outputDir);
};
function TestOutput() {
assert.ok(this instanceof TestOutput);
AbstractOutput.call(this);
}
var TOp = util.inherits(TestOutput, AbstractOutput);
exports.TestOutput = TestOutput;
TOp.outputModule = function(module) {
// Swallow any output.
};
+326
View File
@@ -0,0 +1,326 @@
var assert = require("assert");
var path = require("path");
var fs = require("fs");
var Q = require("q");
var iconv = require("iconv-lite");
var createHash = require("crypto").createHash;
var detective = require("detective");
var util = require("./util");
var BuildContext = require("./context").BuildContext;
var slice = Array.prototype.slice;
function getRequiredIDs(id, source) {
var ids = {};
detective(source).forEach(function (dep) {
ids[path.normalize(path.join(id, "..", dep))] = true;
});
return Object.keys(ids);
}
function ModuleReader(context, resolvers, processors) {
var self = this;
assert.ok(self instanceof ModuleReader);
assert.ok(context instanceof BuildContext);
assert.ok(resolvers instanceof Array);
assert.ok(processors instanceof Array);
var hash = createHash("sha1").update(context.optionsHash + "\0");
function hashCallbacks(salt) {
hash.update(salt + "\0");
var cbs = util.flatten(slice.call(arguments, 1));
cbs.forEach(function(cb) {
assert.strictEqual(typeof cb, "function");
hash.update(cb + "\0");
});
return cbs;
}
resolvers = hashCallbacks("resolvers", resolvers, warnMissingModule);
var procArgs = [processors];
if (context.relativize && !context.ignoreDependencies)
procArgs.push(require("./relative").getProcessor(self));
processors = hashCallbacks("processors", procArgs);
Object.defineProperties(self, {
context: { value: context },
idToHash: { value: {} },
resolvers: { value: resolvers },
processors: { value: processors },
salt: { value: hash.digest("hex") }
});
}
ModuleReader.prototype = {
getSourceP: util.cachedMethod(function(id) {
var context = this.context;
var copy = this.resolvers.slice(0).reverse();
assert.ok(copy.length > 0, "no source resolvers registered");
function tryNextResolverP() {
var resolve = copy.pop();
try {
var promise = Q(resolve && resolve.call(context, id));
} catch (e) {
promise = Q.reject(e);
}
return resolve ? promise.then(function(result) {
if (typeof result === "string")
return result;
return tryNextResolverP();
}, tryNextResolverP) : promise;
}
return tryNextResolverP();
}),
getCanonicalIdP: util.cachedMethod(function(id) {
var reader = this;
if (reader.context.useProvidesModule) {
return reader.getSourceP(id).then(function(source) {
return reader.context.getProvidedId(source) || id;
});
} else {
return Q(id);
}
}),
readModuleP: util.cachedMethod(function(id) {
var reader = this;
return reader.getSourceP(id).then(function(source) {
if (reader.context.useProvidesModule) {
// If the source contains a @providesModule declaration, treat
// that declaration as canonical. Note that the Module object
// returned by readModuleP might have an .id property whose
// value differs from the original id parameter.
id = reader.context.getProvidedId(source) || id;
}
assert.strictEqual(typeof source, "string");
var hash = createHash("sha1")
.update("module\0")
.update(id + "\0")
.update(reader.salt + "\0")
.update(source.length + "\0" + source)
.digest("hex");
if (reader.idToHash.hasOwnProperty(id)) {
// Ensure that the same module identifier is not
// provided by distinct modules.
assert.strictEqual(
reader.idToHash[id], hash,
"more than one module named " +
JSON.stringify(id));
} else {
reader.idToHash[id] = hash;
}
return reader.buildModuleP(id, hash, source);
});
}),
buildModuleP: util.cachedMethod(function(id, hash, source) {
var reader = this;
return reader.processOutputP(
id, hash, source
).then(function(output) {
return new Module(reader, id, hash, output);
});
}, function(id, hash, source) {
return hash;
}),
processOutputP: function(id, hash, source) {
var reader = this;
var cacheDir = reader.context.cacheDir;
var manifestDir = cacheDir && path.join(cacheDir, "manifest");
var charset = reader.context.options.outputCharset;
function buildP() {
var promise = Q(source);
reader.processors.forEach(function(build) {
promise = promise.then(function(input) {
return util.waitForValuesP(
build.call(reader.context, id, input)
);
});
});
return promise.then(function(output) {
if (typeof output === "string") {
output = { ".js": output };
} else {
assert.strictEqual(typeof output, "object");
}
return util.waitForValuesP(output);
}).then(function(output) {
util.log.err(
"built Module(" + JSON.stringify(id) + ")",
"cyan"
);
return output;
}).catch(function(err) {
// Provide additional context for uncaught build errors.
util.log.err("Error while reading module " + id + ":");
throw err;
});
}
if (manifestDir) {
return util.mkdirP(manifestDir).then(function(manifestDir) {
var manifestFile = path.join(manifestDir, hash + ".json");
return util.readJsonFileP(manifestFile).then(function(manifest) {
Object.keys(manifest).forEach(function(key) {
var cacheFile = path.join(cacheDir, manifest[key]);
manifest[key] = util.readFileP(cacheFile);
});
return util.waitForValuesP(manifest, true);
}).catch(function(err) {
return buildP().then(function(output) {
var manifest = {};
Object.keys(output).forEach(function(key) {
var cacheFile = manifest[key] = hash + key;
var fullPath = path.join(cacheDir, cacheFile);
if (charset) {
fs.writeFileSync(fullPath, iconv.encode(output[key], charset))
} else {
fs.writeFileSync(fullPath, output[key], "utf8");
}
});
fs.writeFileSync(
manifestFile,
JSON.stringify(manifest),
"utf8"
);
return output;
});
});
});
}
return buildP();
},
readMultiP: function(ids) {
var reader = this;
return Q(ids).all().then(function(ids) {
if (ids.length === 0)
return ids; // Shortcut.
var modulePs = ids.map(reader.readModuleP, reader);
return Q(modulePs).all().then(function(modules) {
var seen = {};
var result = [];
modules.forEach(function(module) {
if (!seen.hasOwnProperty(module.id)) {
seen[module.id] = true;
result.push(module);
}
});
return result;
});
});
}
};
exports.ModuleReader = ModuleReader;
function warnMissingModule(id) {
// A missing module may be a false positive and therefore does not warrant
// a fatal error, but a warning is certainly in order.
util.log.err(
"unable to resolve module " + JSON.stringify(id) + "; false positive?",
"yellow");
// Missing modules are installed as if they existed, but it's a run-time
// error if one is ever actually required.
var message = "nonexistent module required: " + id;
return "throw new Error(" + JSON.stringify(message) + ");";
}
function Module(reader, id, hash, output) {
assert.ok(this instanceof Module);
assert.ok(reader instanceof ModuleReader);
assert.strictEqual(typeof output, "object");
var source = output[".js"];
assert.strictEqual(typeof source, "string");
Object.defineProperties(this, {
reader: { value: reader },
id: { value: id },
hash: { value: hash }, // TODO Remove?
deps: { value: getRequiredIDs(id, source) },
source: { value: source },
output: { value: output }
});
}
Module.prototype = {
getRequiredP: function() {
return this.reader.readMultiP(this.deps);
},
writeVersionP: function(outputDir) {
var id = this.id;
var hash = this.hash;
var output = this.output;
var cacheDir = this.reader.context.cacheDir;
var charset = this.reader.context.options.outputCharset;
return Q.all(Object.keys(output).map(function(key) {
var outputFile = path.join(outputDir, id + key);
function writeCopy() {
if (charset) {
fs.writeFileSync(outputFile, iconv.encode(output[key], charset));
} else {
fs.writeFileSync(outputFile, output[key], "utf8");
}
return outputFile;
}
if (cacheDir) {
var cacheFile = path.join(cacheDir, hash + key);
return util.linkP(cacheFile, outputFile)
// If the hard linking fails, the cache directory
// might be on a different device, so fall back to
// writing a copy of the file (slightly slower).
.catch(writeCopy);
}
return util.mkdirP(path.dirname(outputFile)).then(writeCopy);
}));
},
toString: function() {
return "Module(" + JSON.stringify(this.id) + ")";
},
resolveId: function(id) {
return util.absolutize(this.id, id);
}
};
+87
View File
@@ -0,0 +1,87 @@
var assert = require("assert");
var Q = require("q");
var path = require("path");
var util = require("./util");
var recast = require("recast");
var n = recast.types.namedTypes;
function Relativizer(reader) {
assert.ok(this instanceof Relativizer);
assert.ok(reader === null ||
reader instanceof require("./reader").ModuleReader);
Object.defineProperties(this, {
reader: { value: reader }
});
}
var Rp = Relativizer.prototype;
exports.getProcessor = function(reader) {
var relativizer = new Relativizer(reader);
return function(id, input) {
return relativizer.processSourceP(id, input);
};
};
Rp.processSourceP = function(id, input) {
var relativizer = this;
var output = typeof input === "string" ? {
".js": input
} : input;
return Q(output[".js"]).then(function(source) {
var promises = [];
var ast = recast.parse(source);
function fixRequireP(literal) {
promises.push(relativizer.relativizeP(
id, literal.value
).then(function(newValue) {
return literal.value = newValue;
}));
}
recast.visit(ast, {
visitCallExpression: function(path) {
var args = path.value.arguments;
var callee = path.value.callee;
if (n.Identifier.check(callee) &&
callee.name === "require" &&
args.length === 1) {
var arg = args[0];
if (n.Literal.check(arg) &&
typeof arg.value === "string") {
fixRequireP(arg);
}
}
this.traverse(path);
}
});
return Q.all(promises).then(function() {
output[".js"] = recast.print(ast).code;
return output;
});
});
};
Rp.absolutizeP = function(moduleId, requiredId) {
requiredId = util.absolutize(moduleId, requiredId);
if (this.reader)
return this.reader.getCanonicalIdP(requiredId);
return Q(requiredId);
};
Rp.relativizeP = function(moduleId, requiredId) {
return this.absolutizeP(
moduleId,
requiredId
).then(function(absoluteId) {
return util.relativize(moduleId, absoluteId);
});
};
+370
View File
@@ -0,0 +1,370 @@
var assert = require("assert");
var path = require("path");
var fs = require("graceful-fs");
var Q = require("q");
var createHash = require("crypto").createHash;
var mkdirp = require("mkdirp");
var iconv = require("iconv-lite");
var Ap = Array.prototype;
var slice = Ap.slice;
var join = Ap.join;
// The graceful-fs module attempts to limit the total number of open files
// by queueing fs operations, but it doesn't know about all open files, so
// we set the limit somewhat lower than the default to provide a healthy
// buffer against EMFILE (too many open files) errors.
fs.MAX_OPEN = 512;
function makePromise(callback, context) {
var deferred = Q.defer();
function finish(err, result) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(result);
}
}
process.nextTick(function() {
try {
callback.call(context || null, finish);
} catch (err) {
finish(err);
}
});
return deferred.promise;
}
exports.makePromise = makePromise;
exports.cachedMethod = function(fn, keyFn) {
var p = require("private").makeAccessor();
function wrapper() {
var priv = p(this);
var cache = priv.cache || (priv.cache = {});
var args = arguments;
var key = keyFn
? keyFn.apply(this, args)
: join.call(args, "\0");
return cache.hasOwnProperty(key)
? cache[key]
: cache[key] = fn.apply(this, args);
}
wrapper.originalFn = fn;
return wrapper;
};
function readFileP(file, charset) {
return makePromise(charset ? function(callback) {
return fs.readFile(file, function(err, data) {
if (err) {
callback(err);
} else {
callback(null, iconv.decode(data, charset));
}
});
} : function(callback) {
return fs.readFile(file, "utf8", callback);
});
}
exports.readFileP = readFileP;
exports.readJsonFileP = function(file) {
return readFileP(file).then(function(json) {
return JSON.parse(json);
});
};
function mkdirP(dir) {
return makePromise(function(callback) {
mkdirp(dir, function(err) {
callback(err, dir);
});
});
}
exports.mkdirP = mkdirP;
function readFromStdinP(timeLimit, message, color) {
var deferred = Q.defer();
var ins = [];
timeLimit = timeLimit || 1000;
var timeout = setTimeout(function() {
log.err(
message || ("Warning: still waiting for STDIN after " +
timeLimit + "ms"),
color || "yellow"
);
}, timeLimit);
try {
// On Windows, just accessing process.stdin throws an exception
// when no standard input has been provided. For consistency with
// other platforms, log the error but continue waiting (until
// killed) for the nonexistent input.
var stdin = process.stdin;
} catch (err) {
log.err(err);
}
if (stdin) {
stdin.resume();
stdin.setEncoding("utf8");
stdin.on("data", function(data) {
ins.push(data);
}).on("end", function() {
clearTimeout(timeout);
deferred.resolve(ins.join(""));
});
}
return deferred.promise;
}
exports.readFromStdinP = readFromStdinP;
exports.readJsonFromStdinP = function(timeLimit) {
return readFromStdinP(timeLimit).then(function(input) {
return JSON.parse(input);
});
};
function deepHash(val) {
var hash = createHash("sha1");
var type = typeof val;
if (val === null) {
type = "null";
}
switch (type) {
case "object":
Object.keys(val).sort().forEach(function(key) {
if (typeof val[key] === "function") {
// Silently ignore nested methods, but nevertheless
// complain below if the root value is a function.
return;
}
hash.update(key + "\0")
.update(deepHash(val[key]));
});
break;
case "function":
assert.ok(false, "cannot hash function objects");
break;
default:
hash.update(val + "");
break;
}
return hash.digest("hex");
}
exports.deepHash = deepHash;
exports.existsP = function(fullPath) {
return makePromise(function(callback) {
fs.exists(fullPath, function(exists) {
callback(null, exists);
});
});
};
function writeFdP(fd, content) {
return makePromise(function(callback) {
content += "";
var buffer = new Buffer(content, "utf8");
var length = fs.writeSync(fd, buffer, 0, buffer.length, null);
assert.strictEqual(length, buffer.length);
callback(null, content);
}).finally(function() {
fs.closeSync(fd);
});
}
exports.writeFdP = writeFdP;
function openFileP(file, mode) {
return makePromise(function(callback) {
fs.open(file, mode || "w+", callback);
});
}
exports.openFileP = openFileP;
function openExclusiveP(file) {
// The 'x' in "wx+" means the file must be newly created.
return openFileP(file, "wx+");
}
exports.openExclusiveP = openExclusiveP;
exports.copyP = function(srcFile, dstFile) {
return makePromise(function(callback) {
var reader = fs.createReadStream(srcFile);
function onError(err) {
callback(err || new Error(
"error in util.copyP(" +
JSON.stringify(srcFile) + ", " +
JSON.stringify(dstFile) + ")"
));
}
reader.on("error", onError).pipe(
fs.createWriteStream(dstFile)
).on("finish", function() {
callback(null, dstFile);
}).on("error", onError);
});
};
// Even though they use synchronous operations to avoid race conditions,
// linkP and unlinkP have promise interfaces, for consistency. Note that
// this means the operation will not happen until at least the next tick
// of the event loop, but it will be atomic when it happens.
exports.linkP = function(srcFile, dstFile) {
return mkdirP(path.dirname(dstFile)).then(function() {
if (fs.existsSync(dstFile))
fs.unlinkSync(dstFile);
fs.linkSync(srcFile, dstFile);
return dstFile;
});
};
exports.unlinkP = function(file) {
return makePromise(function(callback) {
try {
if (fs.existsSync(file))
fs.unlinkSync(file);
callback(null, file);
} catch (err) {
callback(err);
}
});
};
var colors = {
bold: "\033[1m",
red: "\033[31m",
green: "\033[32m",
yellow: "\033[33m",
cyan: "\033[36m",
reset: "\033[0m"
};
Object.keys(colors).forEach(function(key) {
if (key !== "reset") {
exports[key] = function(text) {
return colors[key] + text + colors.reset;
};
}
});
var log = exports.log = {
out: function(text, color) {
text = (text + "").trim();
if (colors.hasOwnProperty(color))
text = colors[color] + text + colors.reset;
process.stdout.write(text + "\n");
},
err: function(text, color) {
text = (text + "").trim();
if (!colors.hasOwnProperty(color))
color = "red";
text = colors[color] + text + colors.reset;
process.stderr.write(text + "\n");
}
};
var slugExp = /[^a-z\-]/ig;
exports.idToSlug = function(id) {
return id.replace(slugExp, "_");
};
var moduleIdExp = /^[ a-z0-9\-_\/\.]+$/i;
exports.isValidModuleId = function(id) {
return id === "<stdin>" || moduleIdExp.test(id);
};
var objToStr = Object.prototype.toString;
var arrStr = objToStr.call([]);
function flatten(value, into) {
if (objToStr.call(value) === arrStr) {
into = into || [];
for (var i = 0, len = value.length; i < len; ++i)
if (i in value) // Skip holes.
flatten(value[i], into);
} else if (into) {
into.push(value);
} else {
return value;
}
return into;
};
exports.flatten = flatten;
exports.inherits = function(ctor, base) {
return ctor.prototype = Object.create(base.prototype, {
constructor: { value: ctor }
});
};
function absolutize(moduleId, requiredId) {
if (requiredId.charAt(0) === ".")
requiredId = path.join(moduleId, "..", requiredId);
return path.normalize(requiredId).replace(/\\/g, '/');
}
exports.absolutize = absolutize;
function relativize(moduleId, requiredId) {
requiredId = absolutize(moduleId, requiredId);
if (requiredId.charAt(0) === ".") {
// Keep the required ID relative.
} else {
// Relativize the required ID.
requiredId = path.relative(
path.join(moduleId, ".."),
requiredId
);
}
if (requiredId.charAt(0) !== ".")
requiredId = "./" + requiredId;
return requiredId.replace(/\\/g, '/');
}
exports.relativize = relativize;
function waitForValuesP(obj, makeCopy) {
if (typeof obj !== "object")
return Q(obj);
var result = makeCopy ? {} : obj;
var keys = Object.keys(obj);
if (keys.length === 0)
return Q(result);
return Q.all(keys.map(function(key) {
return obj[key];
})).then(function(values) {
for (var i = values.length - 1; i >= 0; --i)
result[keys[i]] = values[i];
return result;
});
}
exports.waitForValuesP = waitForValuesP;
function camelize(hyphenated) {
return hyphenated.replace(/-(.)/g, function(_, ch) {
return ch.toUpperCase();
});
}
exports.camelize = camelize;
+255
View File
@@ -0,0 +1,255 @@
var assert = require("assert");
var path = require("path");
var fs = require("graceful-fs");
var spawn = require("child_process").spawn;
var Q = require("q");
var EventEmitter = require("events").EventEmitter;
var ReadFileCache = require("./cache").ReadFileCache;
var util = require("./util");
var hasOwn = Object.prototype.hasOwnProperty;
function Watcher(readFileCache, persistent) {
assert.ok(this instanceof Watcher);
assert.ok(this instanceof EventEmitter);
assert.ok(readFileCache instanceof ReadFileCache);
// During tests (and only during tests), persistent === false so that
// the test suite can actually finish and exit.
if (typeof persistent === "undefined") {
persistent = true;
}
EventEmitter.call(this);
var self = this;
var sourceDir = readFileCache.sourceDir;
var dirWatcher = new DirWatcher(sourceDir, persistent);
Object.defineProperties(self, {
sourceDir: { value: sourceDir },
readFileCache: { value: readFileCache },
dirWatcher: { value: dirWatcher }
});
// Watch everything the readFileCache already knows about, and any new
// files added in the future.
readFileCache.subscribe(function(relativePath) {
self.watch(relativePath);
});
readFileCache.on("changed", function(relativePath) {
self.emit("changed", relativePath);
});
function handleDirEvent(event, relativePath) {
if (self.dirWatcher.ready) {
self.getFileHandler(relativePath)(event);
}
}
dirWatcher.on("added", function(relativePath) {
handleDirEvent("added", relativePath);
}).on("deleted", function(relativePath) {
handleDirEvent("deleted", relativePath);
}).on("changed", function(relativePath) {
handleDirEvent("changed", relativePath);
});
}
util.inherits(Watcher, EventEmitter);
var Wp = Watcher.prototype;
Wp.watch = function(relativePath) {
this.dirWatcher.add(path.dirname(path.join(
this.sourceDir, relativePath)));
};
Wp.readFileP = function(relativePath) {
return this.readFileCache.readFileP(relativePath);
};
Wp.noCacheReadFileP = function(relativePath) {
return this.readFileCache.noCacheReadFileP(relativePath);
};
Wp.getFileHandler = util.cachedMethod(function(relativePath) {
var self = this;
return function handler(event) {
self.readFileCache.reportPossiblyChanged(relativePath);
};
});
function orNull(err) {
return null;
}
Wp.close = function() {
this.dirWatcher.close();
};
/**
* DirWatcher code adapted from Jeffrey Lin's original implementation:
* https://github.com/jeffreylin/jsx_transformer_fun/blob/master/dirWatcher.js
*
* Invariant: this only watches the dir inode, not the actual path.
* That means the dir can't be renamed and swapped with another dir.
*/
function DirWatcher(inputPath, persistent) {
assert.ok(this instanceof DirWatcher);
var self = this;
var absPath = path.resolve(inputPath);
if (!fs.statSync(absPath).isDirectory()) {
throw new Error(inputPath + "is not a directory!");
}
EventEmitter.call(self);
self.ready = false;
self.on("ready", function(){
self.ready = true;
});
Object.defineProperties(self, {
// Map of absDirPaths to fs.FSWatcher objects from fs.watch().
watchers: { value: {} },
dirContents: { value: {} },
rootPath: { value: absPath },
persistent: { value: !!persistent }
});
process.nextTick(function() {
self.add(absPath);
self.emit("ready");
});
}
util.inherits(DirWatcher, EventEmitter);
var DWp = DirWatcher.prototype;
DWp.add = function(absDirPath) {
var self = this;
if (hasOwn.call(self.watchers, absDirPath)) {
return;
}
self.watchers[absDirPath] = fs.watch(absDirPath, {
persistent: this.persistent
}).on("change", function(event, filename) {
self.updateDirContents(absDirPath, event, filename);
});
// Update internal dir contents.
self.updateDirContents(absDirPath);
// Since we've never seen this path before, recursively add child
// directories of this path. TODO: Don't do fs.readdirSync on the
// same dir twice in a row. We already do an fs.statSync in
// this.updateDirContents() and we're just going to do another one
// here...
fs.readdirSync(absDirPath).forEach(function(filename) {
var filepath = path.join(absDirPath, filename);
// Look for directories.
if (fs.statSync(filepath).isDirectory()) {
self.add(filepath);
}
});
};
DWp.updateDirContents = function(absDirPath, event, fsWatchReportedFilename) {
var self = this;
if (!hasOwn.call(self.dirContents, absDirPath)) {
self.dirContents[absDirPath] = [];
}
var oldContents = self.dirContents[absDirPath];
var newContents = fs.readdirSync(absDirPath);
var deleted = {};
var added = {};
oldContents.forEach(function(filename) {
deleted[filename] = true;
});
newContents.forEach(function(filename) {
if (hasOwn.call(deleted, filename)) {
delete deleted[filename];
} else {
added[filename] = true;
}
});
var deletedNames = Object.keys(deleted);
deletedNames.forEach(function(filename) {
self.emit(
"deleted",
path.relative(
self.rootPath,
path.join(absDirPath, filename)
)
);
});
var addedNames = Object.keys(added);
addedNames.forEach(function(filename) {
self.emit(
"added",
path.relative(
self.rootPath,
path.join(absDirPath, filename)
)
);
});
// So changed is not deleted or added?
if (fsWatchReportedFilename &&
!hasOwn.call(deleted, fsWatchReportedFilename) &&
!hasOwn.call(added, fsWatchReportedFilename))
{
self.emit(
"changed",
path.relative(
self.rootPath,
path.join(absDirPath, fsWatchReportedFilename)
)
);
}
// If any of the things removed were directories, remove their watchers.
// If a dir was moved, hopefully two changed events fired?
// 1) event in dir where it was removed
// 2) event in dir where it was moved to (added)
deletedNames.forEach(function(filename) {
var filepath = path.join(absDirPath, filename);
delete self.dirContents[filepath];
delete self.watchers[filepath];
});
// if any of the things added were directories, recursively deal with them
addedNames.forEach(function(filename) {
var filepath = path.join(absDirPath, filename);
if (fs.existsSync(filepath) &&
fs.statSync(filepath).isDirectory())
{
self.add(filepath);
// mighttttttt need a self.updateDirContents() here in case
// we're somehow adding a path that replaces another one...?
}
});
// Update state of internal dir contents.
self.dirContents[absDirPath] = newContents;
};
DWp.close = function() {
var watchers = this.watchers;
Object.keys(watchers).forEach(function(filename) {
watchers[filename].close();
});
};
exports.Watcher = Watcher;
Generated Vendored Executable
+16
View File
@@ -0,0 +1,16 @@
var path = require("path");
var Commoner = require("./lib/commoner").Commoner;
exports.Commoner = Commoner;
function defCallback(name) {
exports[name] = function() {
var commoner = new Commoner;
commoner[name].apply(commoner, arguments);
commoner.cliBuildP();
return commoner;
};
}
defCallback("version");
defCallback("resolve");
defCallback("process");
+1
View File
@@ -0,0 +1 @@
../acorn/bin/acorn
+79
View File
@@ -0,0 +1,79 @@
List of Acorn contributors. Updated before every release.
Adrian Heine
Adrian Rakovsky
Alistair Braidwood
Amila Welihinda
Andres Suarez
Angelo
Aparajita Fishman
Arian Stolwijk
Artem Govorov
Boopesh Mahendran
Bradley Heinz
Brandon Mills
Charles Hughes
Charmander
Chris McKnight
Conrad Irwin
Daniel Tschinder
David Bonnet
Domenico Matteo
ehmicky
Eugene Obrezkov
Felix Maier
Forbes Lindesay
Gilad Peleg
impinball
Ingvar Stepanyan
Jackson Ray Hamilton
Jesse McCarthy
Jiaxing Wang
Joel Kemp
Johannes Herr
John-David Dalton
Jordan Klassen
Jürg Lehni
Kai Cataldo
keeyipchan
Keheliya Gallaba
Kevin Irish
Kevin Kwok
krator
laosb
luckyzeng
Marek
Marijn Haverbeke
Martin Carlberg
Mat Garcia
Mathias Bynens
Mathieu 'p01' Henri
Matthew Bastien
Max Schaefer
Max Zerzouri
Mihai Bazon
Mike Rennie
naoh
Nicholas C. Zakas
Nick Fitzgerald
Olivier Thomann
Oskar Schöldström
Paul Harper
Peter Rust
PlNG
Prayag Verma
ReadmeCritic
r-e-d
Renée Kooi
Richard Gibson
Rich Harris
Sebastian McKenzie
Shahar Soel
Sheel Bedi
Simen Bekkhus
Teddy Katz
Timothy Gu
Toru Nagashima
Victor Homyakov
Wexpo Lyu
zsjforcn
+508
View File
@@ -0,0 +1,508 @@
## 5.7.3 (2018-09-10)
### Bug fixes
Fix failure to tokenize regexps after expressions like `x.of`.
Better error message for unterminated template literals.
## 5.7.2 (2018-08-24)
### Bug fixes
Properly handle `allowAwaitOutsideFunction` in for statements.
Treat function declarations at the top level of modules like let bindings.
Don't allow async function declarations as the only statement under a label.
## 5.7.1 (2018-06-15)
### Bug fixes
Make sure the walker and bin files are rebuilt on release (the previous release didn't get the up-to-date versions).
## 5.7.0 (2018-06-15)
### Bug fixes
Fix crash in walker when walking a binding-less catch node.
### New features
Upgraded to Unicode 11.
## 5.6.2 (2018-06-05)
### Bug fixes
In the walker, go back to allowing the `baseVisitor` argument to be null to default to the default base everywhere.
## 5.6.1 (2018-06-01)
### Bug fixes
Fix regression when passing `null` as fourth argument to `walk.recursive`.
## 5.6.0 (2018-05-31)
### Bug fixes
Fix a bug in the walker that caused a crash when walking an object pattern spread.
### New features
Allow U+2028 and U+2029 in string when ECMAVersion >= 10.
Allow binding-less catch statements when ECMAVersion >= 10.
Add `allowAwaitOutsideFunction` option for parsing top-level `await`.
## 5.5.3 (2018-03-08)
### Bug fixes
A _second_ republish of the code in 5.5.1, this time with yarn, to hopefully get valid timestamps.
## 5.5.2 (2018-03-08)
### Bug fixes
A republish of the code in 5.5.1 in an attempt to solve an issue with the file timestamps in the npm package being 0.
## 5.5.1 (2018-03-06)
### Bug fixes
Fix regression in walker causing property values in object patterns to be walked as expressions.
Fix misleading error message for octal escapes in template strings.
## 5.5.0 (2018-02-27)
### Bug fixes
Support object spread in the AST walker.
### New features
The identifier character categorization is now based on Unicode version 10.
Acorn will now validate the content of regular expressions, including new ES9 features.
## 5.4.1 (2018-02-02)
### Bug fixes
5.4.0 somehow accidentally included an old version of walk.js.
## 5.4.0 (2018-02-01)
### Bug fixes
Disallow duplicate or escaped flags on regular expressions.
Disallow octal escapes in strings in strict mode.
### New features
Add support for async iteration.
Add support for object spread and rest.
## 5.3.0 (2017-12-28)
### Bug fixes
Fix parsing of floating point literals with leading zeroes in loose mode.
Allow duplicate property names in object patterns.
Don't allow static class methods named `prototype`.
Disallow async functions directly under `if` or `else`.
Parse right-hand-side of `for`/`of` as an assignment expression.
Stricter parsing of `for`/`in`.
Don't allow unicode escapes in contextual keywords.
### New features
Parsing class members was factored into smaller methods to allow plugins to hook into it.
## 5.2.1 (2017-10-30)
### Bug fixes
Fix a token context corruption bug.
## 5.2.0 (2017-10-30)
### Bug fixes
Fix token context tracking for `class` and `function` in property-name position.
Make sure `%*` isn't parsed as a valid operator.
The `full` and `fullAncestor` walkers no longer visit nodes multiple times.
Allow shorthand properties `get` and `set` to be followed by default values.
Disallow `super` when not in callee or object position.
### New features
Support [`directive` property](https://github.com/estree/estree/compare/b3de58c9997504d6fba04b72f76e6dd1619ee4eb...1da8e603237144f44710360f8feb7a9977e905e0) on directive expression statements.
## 5.1.2 (2017-09-04)
### Bug fixes
Disable parsing of legacy HTML-style comments in modules.
Fix parsing of async methods whose names are keywords.
## 5.1.1 (2017-07-06)
### Bug fixes
Fix problem with disambiguating regexp and division after a class.
## 5.1.0 (2017-07-05)
### Bug fixes
Fix tokenizing of regexps in an object-desctructuring `for`/`of` loop and after `yield`.
Parse zero-prefixed numbers with non-octal digits as decimal.
Allow object/array patterns in rest parameters.
Don't error when `yield` is used as a property name.
Allow `async` as a shorthand object property.
Make the ES module version of the loose parser actually work.
### New features
Implement the [template literal revision proposal](https://github.com/tc39/proposal-template-literal-revision) for ES9.
New walker functions `full` and `fullAncestor`.
## 5.0.3 (2017-04-01)
### Bug fixes
Fix spurious duplicate variable definition errors for named functions.
## 5.0.2 (2017-03-30)
### Bug fixes
A binary operator after a parenthesized arrow expression is no longer incorrectly treated as an error.
## 5.0.0 (2017-03-28)
### Bug fixes
Raise an error for duplicated lexical bindings.
Fix spurious error when an assignement expression occurred after a spread expression.
Accept regular expressions after `of` (in `for`/`of`), `yield` (in a generator), and braced arrow functions.
Allow labels in front or `var` declarations, even in strict mode.
### Breaking changes
Parse declarations following `export default` as declaration nodes, not expressions. This means that class and function declarations nodes can now have `null` as their `id`.
## 4.0.11 (2017-02-07)
### Bug fixes
Allow all forms of member expressions to be parenthesized as lvalue.
## 4.0.10 (2017-02-07)
### Bug fixes
Don't expect semicolons after default-exported functions or classes,
even when they are expressions.
Check for use of `'use strict'` directives in non-simple parameter
functions, even when already in strict mode.
## 4.0.9 (2017-02-06)
### Bug fixes
Fix incorrect error raised for parenthesized simple assignment
targets, so that `(x) = 1` parses again.
## 4.0.8 (2017-02-03)
### Bug fixes
Solve spurious parenthesized pattern errors by temporarily erring on
the side of accepting programs that our delayed errors don't handle
correctly yet.
## 4.0.7 (2017-02-02)
### Bug fixes
Accept invalidly rejected code like `(x).y = 2` again.
Don't raise an error when a function _inside_ strict code has a
non-simple parameter list.
## 4.0.6 (2017-02-02)
### Bug fixes
Fix exponential behavior (manifesting itself as a complete hang for
even relatively small source files) introduced by the new 'use strict'
check.
## 4.0.5 (2017-02-02)
### Bug fixes
Disallow parenthesized pattern expressions.
Allow keywords as export names.
Don't allow the `async` keyword to be parenthesized.
Properly raise an error when a keyword contains a character escape.
Allow `"use strict"` to appear after other string literal expressions.
Disallow labeled declarations.
## 4.0.4 (2016-12-19)
### Bug fixes
Fix issue with loading acorn_loose.js with an AMD loader.
Fix crash when `export` was followed by a keyword that can't be
exported.
## 4.0.3 (2016-08-16)
### Bug fixes
Allow regular function declarations inside single-statement `if`
branches in loose mode. Forbid them entirely in strict mode.
Properly parse properties named `async` in ES2017 mode.
Fix bug where reserved words were broken in ES2017 mode.
## 4.0.2 (2016-08-11)
### Bug fixes
Don't ignore period or 'e' characters after octal numbers.
Fix broken parsing for call expressions in default parameter values
of arrow functions.
## 4.0.1 (2016-08-08)
### Bug fixes
Fix false positives in duplicated export name errors.
## 4.0.0 (2016-08-07)
### Breaking changes
The default `ecmaVersion` option value is now 7.
A number of internal method signatures changed, so plugins might need
to be updated.
### Bug fixes
The parser now raises errors on duplicated export names.
`arguments` and `eval` can now be used in shorthand properties.
Duplicate parameter names in non-simple argument lists now always
produce an error.
### New features
The `ecmaVersion` option now also accepts year-style version numbers
(2015, etc).
Support for `async`/`await` syntax when `ecmaVersion` is >= 8.
Support for trailing commas in call expressions when `ecmaVersion`
is >= 8.
## 3.3.0 (2016-07-25)
### Bug fixes
Fix bug in tokenizing of regexp operator after a function declaration.
Fix parser crash when parsing an array pattern with a hole.
### New features
Implement check against complex argument lists in functions that
enable strict mode in ES7.
## 3.2.0 (2016-06-07)
### Bug fixes
Improve handling of lack of unicode regexp support in host
environment.
Properly reject shorthand properties whose name is a keyword.
Don't crash when the loose parser is called without options object.
### New features
Visitors created with `visit.make` now have their base as _prototype_,
rather than copying properties into a fresh object.
Make it possible to use `visit.ancestor` with a walk state.
## 3.1.0 (2016-04-18)
### Bug fixes
Fix issue where the loose parser created invalid TemplateElement nodes
for unclosed template literals.
Properly tokenize the division operator directly after a function
expression.
Allow trailing comma in destructuring arrays.
### New features
The walker now allows defining handlers for `CatchClause` nodes.
## 3.0.4 (2016-02-25)
### Fixes
Allow update expressions as left-hand-side of the ES7 exponential
operator.
## 3.0.2 (2016-02-10)
### Fixes
Fix bug that accidentally made `undefined` a reserved word when
parsing ES7.
## 3.0.0 (2016-02-10)
### Breaking changes
The default value of the `ecmaVersion` option is now 6 (used to be 5).
Support for comprehension syntax (which was dropped from the draft
spec) has been removed.
### Fixes
`let` and `yield` are now “contextual keywords”, meaning you can
mostly use them as identifiers in ES5 non-strict code.
A parenthesized class or function expression after `export default` is
now parsed correctly.
### New features
When `ecmaVersion` is set to 7, Acorn will parse the exponentiation
operator (`**`).
The identifier character ranges are now based on Unicode 8.0.0.
Plugins can now override the `raiseRecoverable` method to override the
way non-critical errors are handled.
## 2.7.0 (2016-01-04)
### Fixes
Stop allowing rest parameters in setters.
Make sure the loose parser always attaches a `local` property to
`ImportNamespaceSpecifier` nodes.
Disallow `y` rexexp flag in ES5.
Disallow `\00` and `\000` escapes in strict mode.
Raise an error when an import name is a reserved word.
## 2.6.4 (2015-11-12)
### Fixes
Fix crash in loose parser when parsing invalid object pattern.
### New features
Support plugins in the loose parser.
## 2.6.2 (2015-11-10)
### Fixes
Don't crash when no options object is passed.
## 2.6.0 (2015-11-09)
### Fixes
Add `await` as a reserved word in module sources.
Disallow `yield` in a parameter default value for a generator.
Forbid using a comma after a rest pattern in an array destructuring.
### New features
Support parsing stdin in command-line tool.
## 2.5.2 (2015-10-27)
### Fixes
Fix bug where the walker walked an exported `let` statement as an
expression.
## 2.5.0 (2015-10-27)
### Fixes
Fix tokenizer support in the command-line tool.
In the loose parser, don't allow non-string-literals as import
sources.
Stop allowing `new.target` outside of functions.
Remove legacy `guard` and `guardedHandler` properties from try nodes.
Stop allowing multiple `__proto__` properties on an object literal in
strict mode.
Don't allow rest parameters to be non-identifier patterns.
Check for duplicate paramter names in arrow functions.
+19
View File
@@ -0,0 +1,19 @@
Copyright (C) 2012-2018 by various contributors (see AUTHORS)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+467
View File
@@ -0,0 +1,467 @@
# Acorn
[![Build Status](https://travis-ci.org/acornjs/acorn.svg?branch=master)](https://travis-ci.org/acornjs/acorn)
[![NPM version](https://img.shields.io/npm/v/acorn.svg)](https://www.npmjs.com/package/acorn)
[![CDNJS](https://img.shields.io/cdnjs/v/acorn.svg)](https://cdnjs.com/libraries/acorn)
[Author funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?force)](https://marijnhaverbeke.nl/fund/)
A tiny, fast JavaScript parser, written completely in JavaScript.
## Community
Acorn is open source software released under an
[MIT license](https://github.com/acornjs/acorn/blob/master/LICENSE).
You are welcome to
[report bugs](https://github.com/acornjs/acorn/issues) or create pull
requests on [github](https://github.com/acornjs/acorn). For questions
and discussion, please use the
[Tern discussion forum](https://discuss.ternjs.net).
## Installation
The easiest way to install acorn is with [`npm`][npm].
[npm]: https://www.npmjs.com/
```sh
npm install acorn
```
Alternately, you can download the source and build acorn yourself:
```sh
git clone https://github.com/acornjs/acorn.git
cd acorn
npm install
npm run build
```
## Components
When run in a CommonJS (node.js) or AMD environment, exported values
appear in the interfaces exposed by the individual files, as usual.
When loaded in the browser (Acorn works in any JS-enabled browser more
recent than IE5) without any kind of module management, a single
global object `acorn` will be defined, and all the exported properties
will be added to that.
### Main parser
This is implemented in `dist/acorn.js`, and is what you get when you
`require("acorn")` in node.js.
**parse**`(input, options)` is used to parse a JavaScript program.
The `input` parameter is a string, `options` can be undefined or an
object setting some of the options listed below. The return value will
be an abstract syntax tree object as specified by the
[ESTree spec][estree].
When encountering a syntax error, the parser will raise a
`SyntaxError` object with a meaningful message. The error object will
have a `pos` property that indicates the character offset at which the
error occurred, and a `loc` object that contains a `{line, column}`
object referring to that same position.
[estree]: https://github.com/estree/estree
- **ecmaVersion**: Indicates the ECMAScript version to parse. Must be
either 3, 5, 6 (2015), 7 (2016), 8 (2017), 9 (2018) or 10 (2019, partial
support). This influences support for strict mode, the set of
reserved words, and support for new syntax features. Default is 7.
**NOTE**: Only 'stage 4' (finalized) ECMAScript features are being
implemented by Acorn.
- **sourceType**: Indicate the mode the code should be parsed in. Can be
either `"script"` or `"module"`. This influences global strict mode
and parsing of `import` and `export` declarations.
- **onInsertedSemicolon**: If given a callback, that callback will be
called whenever a missing semicolon is inserted by the parser. The
callback will be given the character offset of the point where the
semicolon is inserted as argument, and if `locations` is on, also a
`{line, column}` object representing this position.
- **onTrailingComma**: Like `onInsertedSemicolon`, but for trailing
commas.
- **allowReserved**: If `false`, using a reserved word will generate
an error. Defaults to `true` for `ecmaVersion` 3, `false` for higher
versions. When given the value `"never"`, reserved words and
keywords can also not be used as property names (as in Internet
Explorer's old parser).
- **allowReturnOutsideFunction**: By default, a return statement at
the top level raises an error. Set this to `true` to accept such
code.
- **allowImportExportEverywhere**: By default, `import` and `export`
declarations can only appear at a program's top level. Setting this
option to `true` allows them anywhere where a statement is allowed.
- **allowAwaitOutsideFunction**: By default, `await` expressions can only appear inside `async` functions. Setting this option to `true` allows to have top-level `await` expressions. They are still not allowed in non-`async` functions, though.
- **allowHashBang**: When this is enabled (off by default), if the
code starts with the characters `#!` (as in a shellscript), the
first line will be treated as a comment.
- **locations**: When `true`, each node has a `loc` object attached
with `start` and `end` subobjects, each of which contains the
one-based line and zero-based column numbers in `{line, column}`
form. Default is `false`.
- **onToken**: If a function is passed for this option, each found
token will be passed in same format as tokens returned from
`tokenizer().getToken()`.
If array is passed, each found token is pushed to it.
Note that you are not allowed to call the parser from the
callback—that will corrupt its internal state.
- **onComment**: If a function is passed for this option, whenever a
comment is encountered the function will be called with the
following parameters:
- `block`: `true` if the comment is a block comment, false if it
is a line comment.
- `text`: The content of the comment.
- `start`: Character offset of the start of the comment.
- `end`: Character offset of the end of the comment.
When the `locations` options is on, the `{line, column}` locations
of the comments start and end are passed as two additional
parameters.
If array is passed for this option, each found comment is pushed
to it as object in Esprima format:
```javascript
{
"type": "Line" | "Block",
"value": "comment text",
"start": Number,
"end": Number,
// If `locations` option is on:
"loc": {
"start": {line: Number, column: Number}
"end": {line: Number, column: Number}
},
// If `ranges` option is on:
"range": [Number, Number]
}
```
Note that you are not allowed to call the parser from the
callback—that will corrupt its internal state.
- **ranges**: Nodes have their start and end characters offsets
recorded in `start` and `end` properties (directly on the node,
rather than the `loc` object, which holds line/column data. To also
add a [semi-standardized][range] `range` property holding a
`[start, end]` array with the same numbers, set the `ranges` option
to `true`.
- **program**: It is possible to parse multiple files into a single
AST by passing the tree produced by parsing the first file as the
`program` option in subsequent parses. This will add the toplevel
forms of the parsed file to the "Program" (top) node of an existing
parse tree.
- **sourceFile**: When the `locations` option is `true`, you can pass
this option to add a `source` attribute in every nodes `loc`
object. Note that the contents of this option are not examined or
processed in any way; you are free to use whatever format you
choose.
- **directSourceFile**: Like `sourceFile`, but a `sourceFile` property
will be added (regardless of the `location` option) directly to the
nodes, rather than the `loc` object.
- **preserveParens**: If this option is `true`, parenthesized expressions
are represented by (non-standard) `ParenthesizedExpression` nodes
that have a single `expression` property containing the expression
inside parentheses.
[range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
**parseExpressionAt**`(input, offset, options)` will parse a single
expression in a string, and return its AST. It will not complain if
there is more of the string left after the expression.
**getLineInfo**`(input, offset)` can be used to get a `{line,
column}` object for a given program string and character offset.
**tokenizer**`(input, options)` returns an object with a `getToken`
method that can be called repeatedly to get the next token, a `{start,
end, type, value}` object (with added `loc` property when the
`locations` option is enabled and `range` property when the `ranges`
option is enabled). When the token's type is `tokTypes.eof`, you
should stop calling the method, since it will keep returning that same
token forever.
In ES6 environment, returned result can be used as any other
protocol-compliant iterable:
```javascript
for (let token of acorn.tokenizer(str)) {
// iterate over the tokens
}
// transform code to array of tokens:
var tokens = [...acorn.tokenizer(str)];
```
**tokTypes** holds an object mapping names to the token type objects
that end up in the `type` properties of tokens.
#### Note on using with [Escodegen][escodegen]
Escodegen supports generating comments from AST, attached in
Esprima-specific format. In order to simulate same format in
Acorn, consider following example:
```javascript
var comments = [], tokens = [];
var ast = acorn.parse('var x = 42; // answer', {
// collect ranges for each node
ranges: true,
// collect comments in Esprima's format
onComment: comments,
// collect token ranges
onToken: tokens
});
// attach comments using collected information
escodegen.attachComments(ast, comments, tokens);
// generate code
console.log(escodegen.generate(ast, {comment: true}));
// > 'var x = 42; // answer'
```
[escodegen]: https://github.com/estools/escodegen
### dist/acorn_loose.js ###
This file implements an error-tolerant parser. It exposes a single
function. The loose parser is accessible in node.js via `require("acorn/dist/acorn_loose")`.
**parse_dammit**`(input, options)` takes the same arguments and
returns the same syntax tree as the `parse` function in `acorn.js`,
but never raises an error, and will do its best to parse syntactically
invalid code in as meaningful a way as it can. It'll insert identifier
nodes with name `"✖"` as placeholders in places where it can't make
sense of the input. Depends on `acorn.js`, because it uses the same
tokenizer.
### dist/walk.js ###
Implements an abstract syntax tree walker. Will store its interface in
`acorn.walk` when loaded without a module system.
**simple**`(node, visitors, base, state)` does a 'simple' walk over
a tree. `node` should be the AST node to walk, and `visitors` an
object with properties whose names correspond to node types in the
[ESTree spec][estree]. The properties should contain functions
that will be called with the node object and, if applicable the state
at that point. The last two arguments are optional. `base` is a walker
algorithm, and `state` is a start state. The default walker will
simply visit all statements and expressions and not produce a
meaningful state. (An example of a use of state is to track scope at
each point in the tree.)
```js
const acorn = require("acorn")
const walk = require("acorn/dist/walk")
walk.simple(acorn.parse("let x = 10"), {
Literal(node) {
console.log(`Found a literal: ${node.value}`)
}
})
```
**ancestor**`(node, visitors, base, state)` does a 'simple' walk over
a tree, building up an array of ancestor nodes (including the current node)
and passing the array to the callbacks as a third parameter.
```js
const acorn = require("acorn")
const walk = require("acorn/dist/walk")
walk.ancestor(acorn.parse("foo('hi')"), {
Literal(_, ancestors) {
console.log("This literal's ancestors are:",
ancestors.map(n => n.type))
}
})
```
**recursive**`(node, state, functions, base)` does a 'recursive'
walk, where the walker functions are responsible for continuing the
walk on the child nodes of their target node. `state` is the start
state, and `functions` should contain an object that maps node types
to walker functions. Such functions are called with `(node, state, c)`
arguments, and can cause the walk to continue on a sub-node by calling
the `c` argument on it with `(node, state)` arguments. The optional
`base` argument provides the fallback walker functions for node types
that aren't handled in the `functions` object. If not given, the
default walkers will be used.
**make**`(functions, base)` builds a new walker object by using the
walker functions in `functions` and filling in the missing ones by
taking defaults from `base`.
**full**`(node, callback, base, state)` does a 'full'
walk over a tree, calling the callback with the arguments (node, state, type)
for each node
**fullAncestor**`(node, callback, base, state)` does a 'full' walk over
a tree, building up an array of ancestor nodes (including the current node)
and passing the array to the callbacks as a third parameter.
```js
const acorn = require("acorn")
const walk = require("acorn/dist/walk")
walk.full(acorn.parse("1 + 1"), node => {
console.log(`There's a ${node.type} node at ${node.ch}`)
})
```
**findNodeAt**`(node, start, end, test, base, state)` tries to
locate a node in a tree at the given start and/or end offsets, which
satisfies the predicate `test`. `start` and `end` can be either `null`
(as wildcard) or a number. `test` may be a string (indicating a node
type) or a function that takes `(nodeType, node)` arguments and
returns a boolean indicating whether this node is interesting. `base`
and `state` are optional, and can be used to specify a custom walker.
Nodes are tested from inner to outer, so if two nodes match the
boundaries, the inner one will be preferred.
**findNodeAround**`(node, pos, test, base, state)` is a lot like
`findNodeAt`, but will match any node that exists 'around' (spanning)
the given position.
**findNodeAfter**`(node, pos, test, base, state)` is similar to
`findNodeAround`, but will match all nodes *after* the given position
(testing outer nodes before inner nodes).
## Command line interface
The `bin/acorn` utility can be used to parse a file from the command
line. It accepts as arguments its input file and the following
options:
- `--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9|--ecma10`: Sets the ECMAScript version
to parse. Default is version 7.
- `--module`: Sets the parsing mode to `"module"`. Is set to `"script"` otherwise.
- `--locations`: Attaches a "loc" object to each node with "start" and
"end" subobjects, each of which contains the one-based line and
zero-based column numbers in `{line, column}` form.
- `--allow-hash-bang`: If the code starts with the characters #! (as in a shellscript), the first line will be treated as a comment.
- `--compact`: No whitespace is used in the AST output.
- `--silent`: Do not output the AST, just return the exit status.
- `--help`: Print the usage information and quit.
The utility spits out the syntax tree as JSON data.
## Build system
Acorn is written in ECMAScript 6, as a set of small modules, in the
project's `src` directory, and compiled down to bigger ECMAScript 3
files in `dist` using [Browserify](http://browserify.org) and
[Babel](http://babeljs.io/). If you are already using Babel, you can
consider including the modules directly.
The command-line test runner (`npm test`) uses the ES6 modules. The
browser-based test page (`test/index.html`) uses the compiled modules.
The `bin/build-acorn.js` script builds the latter from the former.
If you are working on Acorn, you'll probably want to try the code out
directly, without an intermediate build step. In your scripts, you can
register the Babel require shim like this:
require("babel-core/register")
That will allow you to directly `require` the ES6 modules.
## Plugins
Acorn is designed support allow plugins which, within reasonable
bounds, redefine the way the parser works. Plugins can add new token
types and new tokenizer contexts (if necessary), and extend methods in
the parser object. This is not a clean, elegant API—using it requires
an understanding of Acorn's internals, and plugins are likely to break
whenever those internals are significantly changed. But still, it is
_possible_, in this way, to create parsers for JavaScript dialects
without forking all of Acorn. And in principle it is even possible to
combine such plugins, so that if you have, for example, a plugin for
parsing types and a plugin for parsing JSX-style XML literals, you
could load them both and parse code with both JSX tags and types.
A plugin should register itself by adding a property to
`acorn.plugins`, which holds a function. Calling `acorn.parse`, a
`plugins` option can be passed, holding an object mapping plugin names
to configuration values (or just `true` for plugins that don't take
options). After the parser object has been created, the initialization
functions for the chosen plugins are called with `(parser,
configValue)` arguments. They are expected to use the `parser.extend`
method to extend parser methods. For example, the `readToken` method
could be extended like this:
```javascript
parser.extend("readToken", function(nextMethod) {
return function(code) {
console.log("Reading a token!")
return nextMethod.call(this, code)
}
})
```
The `nextMethod` argument passed to `extend`'s second argument is the
previous value of this method, and should usually be called through to
whenever the extended method does not handle the call itself.
Similarly, the loose parser allows plugins to register themselves via
`acorn.pluginsLoose`. The extension mechanism is the same as for the
normal parser:
```javascript
looseParser.extend("readToken", function(nextMethod) {
return function() {
console.log("Reading a token in the loose parser!")
return nextMethod.call(this)
}
})
```
### Existing plugins
- [`acorn-jsx`](https://github.com/RReverser/acorn-jsx): Parse [Facebook JSX syntax extensions](https://github.com/facebook/jsx)
- [`acorn-objj`](https://github.com/cappuccino/acorn-objj): [Objective-J](http://www.cappuccino-project.org/learn/objective-j.html) language parser built as Acorn plugin
Plugins for ECMAScript proposals:
- [`acorn-stage3`](https://github.com/acornjs/acorn-stage3): Parse most stage 3 proposals, bundling:
- [`acorn-async-iteration`](https://github.com/acornjs/acorn-async-iteration): Parse [async iteration proposal](https://github.com/tc39/proposal-async-iteration)
- [`acorn-bigint`](https://github.com/acornjs/acorn-bigint): Parse [BigInt proposal](https://github.com/tc39/proposal-bigint)
- [`acorn-class-fields`](https://github.com/acornjs/acorn-class-fields): Parse [class fields proposal](https://github.com/tc39/proposal-class-fields)
- [`acorn-dynamic-import`](https://github.com/kesne/acorn-dynamic-import): Parse [import() proposal](https://github.com/tc39/proposal-dynamic-import)
- [`acorn-import-meta`](https://github.com/acornjs/acorn-import-meta): Parse [import.meta proposal](https://github.com/tc39/proposal-import-meta)
- [`acorn-numeric-separator`](https://github.com/acornjs/acorn-numeric-separator): Parse [numeric separator proposal](https://github.com/tc39/proposal-numeric-separator)
- [`acorn-optional-catch-binding`](https://github.com/acornjs/acorn-optional-catch-binding): Parse [optional catch binding proposal](https://github.com/tc39/proposal-optional-catch-binding)
- [`acorn-private-methods`](https://github.com/acornjs/acorn-private-methods): parse [private methods, getters and setters proposal](https://github.com/tc39/proposal-private-methods)
- [`acorn5-object-spread`](https://github.com/adrianheine/acorn5-object-spread): Parse [Object Rest/Spread Properties proposal](https://github.com/tc39/proposal-object-rest-spread)
- [`acorn-object-rest-spread`](https://github.com/victor-homyakov/acorn-object-rest-spread): Parse [Object Rest/Spread Properties proposal](https://github.com/tc39/proposal-object-rest-spread)
- [`acorn-es7`](https://github.com/angelozerr/acorn-es7): Parse [decorator syntax proposal](https://github.com/wycats/javascript-decorators)
- [`acorn-static-class-property-initializer`](https://github.com/victor-homyakov/acorn-static-class-property-initializer): Partial support for static class properties from [ES Class Fields & Static Properties Proposal](https://github.com/tc39/proposal-class-public-fields) to support static property initializers in [React components written as ES6+ classes](https://babeljs.io/blog/2015/07/07/react-on-es6-plus)
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env node
'use strict';
var path = require('path');
var fs = require('fs');
var acorn = require('../dist/acorn.js');
var infile;
var forceFile;
var silent = false;
var compact = false;
var tokenize = false;
var options = {};
function help(status) {
var print = (status === 0) ? console.log : console.error;
print("usage: " + path.basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9|...|--ecma2015|--ecma2016|--ecma2017|--ecma2018|...]");
print(" [--tokenize] [--locations] [---allow-hash-bang] [--compact] [--silent] [--module] [--help] [--] [infile]");
process.exit(status);
}
for (var i = 2; i < process.argv.length; ++i) {
var arg = process.argv[i];
if ((arg === "-" || arg[0] !== "-") && !infile) { infile = arg; }
else if (arg === "--" && !infile && i + 2 === process.argv.length) { forceFile = infile = process.argv[++i]; }
else if (arg === "--locations") { options.locations = true; }
else if (arg === "--allow-hash-bang") { options.allowHashBang = true; }
else if (arg === "--silent") { silent = true; }
else if (arg === "--compact") { compact = true; }
else if (arg === "--help") { help(0); }
else if (arg === "--tokenize") { tokenize = true; }
else if (arg === "--module") { options.sourceType = "module"; }
else {
var match = arg.match(/^--ecma(\d+)$/);
if (match)
{ options.ecmaVersion = +match[1]; }
else
{ help(1); }
}
}
function run(code) {
var result;
try {
if (!tokenize) {
result = acorn.parse(code, options);
} else {
result = [];
var tokenizer$$1 = acorn.tokenizer(code, options), token;
do {
token = tokenizer$$1.getToken();
result.push(token);
} while (token.type !== acorn.tokTypes.eof)
}
} catch (e) {
console.error(e.message);
process.exit(1);
}
if (!silent) { console.log(JSON.stringify(result, null, compact ? null : 2)); }
}
if (forceFile || infile && infile !== "-") {
run(fs.readFileSync(infile, "utf8"));
} else {
var code = "";
process.stdin.resume();
process.stdin.on("data", function (chunk) { return code += chunk; });
process.stdin.on("end", function () { return run(code); });
}
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env node
'use strict';
require('./_acorn.js');
+21
View File
@@ -0,0 +1,21 @@
const fs = require("fs")
const path = require("path")
const run = require("test262-parser-runner")
const parse = require("..").parse
const unsupportedFeatures = [
"BigInt",
"class-fields",
"class-fields-private",
"class-fields-public",
"numeric-separator-literal"
];
run(
(content, {sourceType}) => parse(content, {sourceType, ecmaVersion: 10}),
{
testsDirectory: path.dirname(require.resolve("test262/package.json")),
skip: test => (test.attrs.features && unsupportedFeatures.some(f => test.attrs.features.includes(f))),
whitelist: fs.readFileSync("./bin/test262.whitelist", "utf8").split("\n").filter(v => v)
}
)
+404
View File
@@ -0,0 +1,404 @@
annexB/language/function-code/block-decl-func-no-skip-try.js (default)
annexB/language/function-code/block-decl-func-skip-early-err-block.js (default)
annexB/language/function-code/block-decl-func-skip-early-err.js (default)
annexB/language/function-code/block-decl-func-skip-early-err-switch.js (default)
annexB/language/function-code/block-decl-func-skip-early-err-try.js (default)
annexB/language/function-code/if-decl-else-decl-a-func-no-skip-try.js (default)
annexB/language/function-code/if-decl-else-decl-a-func-skip-early-err-block.js (default)
annexB/language/function-code/if-decl-else-decl-a-func-skip-early-err.js (default)
annexB/language/function-code/if-decl-else-decl-a-func-skip-early-err-switch.js (default)
annexB/language/function-code/if-decl-else-decl-a-func-skip-early-err-try.js (default)
annexB/language/function-code/if-decl-else-decl-b-func-no-skip-try.js (default)
annexB/language/function-code/if-decl-else-decl-b-func-skip-early-err-block.js (default)
annexB/language/function-code/if-decl-else-decl-b-func-skip-early-err.js (default)
annexB/language/function-code/if-decl-else-decl-b-func-skip-early-err-switch.js (default)
annexB/language/function-code/if-decl-else-decl-b-func-skip-early-err-try.js (default)
annexB/language/function-code/if-decl-else-stmt-func-no-skip-try.js (default)
annexB/language/function-code/if-decl-else-stmt-func-skip-early-err-block.js (default)
annexB/language/function-code/if-decl-else-stmt-func-skip-early-err.js (default)
annexB/language/function-code/if-decl-else-stmt-func-skip-early-err-switch.js (default)
annexB/language/function-code/if-decl-else-stmt-func-skip-early-err-try.js (default)
annexB/language/function-code/if-decl-no-else-func-no-skip-try.js (default)
annexB/language/function-code/if-decl-no-else-func-skip-early-err-block.js (default)
annexB/language/function-code/if-decl-no-else-func-skip-early-err.js (default)
annexB/language/function-code/if-decl-no-else-func-skip-early-err-switch.js (default)
annexB/language/function-code/if-decl-no-else-func-skip-early-err-try.js (default)
annexB/language/function-code/if-stmt-else-decl-func-no-skip-try.js (default)
annexB/language/function-code/if-stmt-else-decl-func-skip-early-err-block.js (default)
annexB/language/function-code/if-stmt-else-decl-func-skip-early-err.js (default)
annexB/language/function-code/if-stmt-else-decl-func-skip-early-err-switch.js (default)
annexB/language/function-code/if-stmt-else-decl-func-skip-early-err-try.js (default)
annexB/language/function-code/switch-case-func-no-skip-try.js (default)
annexB/language/function-code/switch-case-func-skip-early-err-block.js (default)
annexB/language/function-code/switch-case-func-skip-early-err.js (default)
annexB/language/function-code/switch-case-func-skip-early-err-switch.js (default)
annexB/language/function-code/switch-case-func-skip-early-err-try.js (default)
annexB/language/function-code/switch-dflt-func-no-skip-try.js (default)
annexB/language/function-code/switch-dflt-func-skip-early-err-block.js (default)
annexB/language/function-code/switch-dflt-func-skip-early-err.js (default)
annexB/language/function-code/switch-dflt-func-skip-early-err-switch.js (default)
annexB/language/function-code/switch-dflt-func-skip-early-err-try.js (default)
annexB/language/global-code/block-decl-global-no-skip-try.js (default)
annexB/language/global-code/block-decl-global-skip-early-err-block.js (default)
annexB/language/global-code/block-decl-global-skip-early-err.js (default)
annexB/language/global-code/block-decl-global-skip-early-err-switch.js (default)
annexB/language/global-code/block-decl-global-skip-early-err-try.js (default)
annexB/language/global-code/if-decl-else-decl-a-global-no-skip-try.js (default)
annexB/language/global-code/if-decl-else-decl-a-global-skip-early-err-block.js (default)
annexB/language/global-code/if-decl-else-decl-a-global-skip-early-err.js (default)
annexB/language/global-code/if-decl-else-decl-a-global-skip-early-err-switch.js (default)
annexB/language/global-code/if-decl-else-decl-a-global-skip-early-err-try.js (default)
annexB/language/global-code/if-decl-else-decl-b-global-no-skip-try.js (default)
annexB/language/global-code/if-decl-else-decl-b-global-skip-early-err-block.js (default)
annexB/language/global-code/if-decl-else-decl-b-global-skip-early-err.js (default)
annexB/language/global-code/if-decl-else-decl-b-global-skip-early-err-switch.js (default)
annexB/language/global-code/if-decl-else-decl-b-global-skip-early-err-try.js (default)
annexB/language/global-code/if-decl-else-stmt-global-no-skip-try.js (default)
annexB/language/global-code/if-decl-else-stmt-global-skip-early-err-block.js (default)
annexB/language/global-code/if-decl-else-stmt-global-skip-early-err.js (default)
annexB/language/global-code/if-decl-else-stmt-global-skip-early-err-switch.js (default)
annexB/language/global-code/if-decl-else-stmt-global-skip-early-err-try.js (default)
annexB/language/global-code/if-decl-no-else-global-no-skip-try.js (default)
annexB/language/global-code/if-decl-no-else-global-skip-early-err-block.js (default)
annexB/language/global-code/if-decl-no-else-global-skip-early-err.js (default)
annexB/language/global-code/if-decl-no-else-global-skip-early-err-switch.js (default)
annexB/language/global-code/if-decl-no-else-global-skip-early-err-try.js (default)
annexB/language/global-code/if-stmt-else-decl-global-no-skip-try.js (default)
annexB/language/global-code/if-stmt-else-decl-global-skip-early-err-block.js (default)
annexB/language/global-code/if-stmt-else-decl-global-skip-early-err.js (default)
annexB/language/global-code/if-stmt-else-decl-global-skip-early-err-switch.js (default)
annexB/language/global-code/if-stmt-else-decl-global-skip-early-err-try.js (default)
annexB/language/global-code/switch-case-global-no-skip-try.js (default)
annexB/language/global-code/switch-case-global-skip-early-err-block.js (default)
annexB/language/global-code/switch-case-global-skip-early-err.js (default)
annexB/language/global-code/switch-case-global-skip-early-err-switch.js (default)
annexB/language/global-code/switch-case-global-skip-early-err-try.js (default)
annexB/language/global-code/switch-dflt-global-no-skip-try.js (default)
annexB/language/global-code/switch-dflt-global-skip-early-err-block.js (default)
annexB/language/global-code/switch-dflt-global-skip-early-err.js (default)
annexB/language/global-code/switch-dflt-global-skip-early-err-switch.js (default)
annexB/language/global-code/switch-dflt-global-skip-early-err-try.js (default)
annexB/language/statements/try/catch-redeclared-for-in-var.js (default)
annexB/language/statements/try/catch-redeclared-for-in-var.js (strict mode)
annexB/language/statements/try/catch-redeclared-for-var.js (default)
annexB/language/statements/try/catch-redeclared-for-var.js (strict mode)
annexB/language/statements/try/catch-redeclared-var-statement-captured.js (default)
annexB/language/statements/try/catch-redeclared-var-statement-captured.js (strict mode)
annexB/language/statements/try/catch-redeclared-var-statement.js (default)
annexB/language/statements/try/catch-redeclared-var-statement.js (strict mode)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/block-scope/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-const-declaration.js (default)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-const-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-let-declaration.js (default)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-let-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/block-scope/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/const-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/block-scope/syntax/redeclaration/const-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/block-scope/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/block-scope/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/let-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/block-scope/syntax/redeclaration/let-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/block-scope/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/expressions/async-arrow-function/early-errors-arrow-await-in-formals-default.js (default)
language/expressions/async-arrow-function/early-errors-arrow-await-in-formals-default.js (strict mode)
language/expressions/async-arrow-function/early-errors-arrow-body-contains-super-call.js (default)
language/expressions/async-arrow-function/early-errors-arrow-body-contains-super-call.js (strict mode)
language/expressions/async-arrow-function/early-errors-arrow-body-contains-super-property.js (default)
language/expressions/async-arrow-function/early-errors-arrow-body-contains-super-property.js (strict mode)
language/expressions/async-function/early-errors-expression-body-contains-super-call.js (default)
language/expressions/async-function/early-errors-expression-body-contains-super-call.js (strict mode)
language/expressions/async-function/early-errors-expression-body-contains-super-property.js (default)
language/expressions/async-function/early-errors-expression-body-contains-super-property.js (strict mode)
language/expressions/async-function/early-errors-expression-formals-contains-super-call.js (default)
language/expressions/async-function/early-errors-expression-formals-contains-super-call.js (strict mode)
language/expressions/async-function/early-errors-expression-formals-contains-super-property.js (default)
language/expressions/async-function/early-errors-expression-formals-contains-super-property.js (strict mode)
language/expressions/class/method-param-dflt-yield.js (default)
language/expressions/class/static-method-param-dflt-yield.js (default)
language/expressions/function/early-body-super-call.js (default)
language/expressions/function/early-body-super-call.js (strict mode)
language/expressions/function/early-body-super-prop.js (default)
language/expressions/function/early-body-super-prop.js (strict mode)
language/expressions/function/early-params-super-call.js (default)
language/expressions/function/early-params-super-call.js (strict mode)
language/expressions/function/early-params-super-prop.js (default)
language/expressions/function/early-params-super-prop.js (strict mode)
language/expressions/object/method-definition/early-errors-object-method-body-contains-super-call.js (default)
language/expressions/object/method-definition/early-errors-object-method-body-contains-super-call.js (strict mode)
language/expressions/object/method-definition/early-errors-object-method-duplicate-parameters.js (default)
language/expressions/object/method-definition/early-errors-object-method-formals-contains-super-call.js (default)
language/expressions/object/method-definition/early-errors-object-method-formals-contains-super-call.js (strict mode)
language/expressions/object/method-definition/generator-super-call-body.js (default)
language/expressions/object/method-definition/generator-super-call-body.js (strict mode)
language/expressions/object/method-definition/generator-super-call-param.js (default)
language/expressions/object/method-definition/generator-super-call-param.js (strict mode)
language/expressions/object/prop-def-invalid-async-prefix.js (default)
language/expressions/object/prop-def-invalid-async-prefix.js (strict mode)
language/expressions/yield/in-iteration-stmt.js (default)
language/expressions/yield/in-iteration-stmt.js (strict mode)
language/expressions/yield/star-in-iteration-stmt.js (default)
language/expressions/yield/star-in-iteration-stmt.js (strict mode)
language/global-code/new.target-arrow.js (default)
language/global-code/new.target-arrow.js (strict mode)
language/global-code/super-call-arrow.js (default)
language/global-code/super-call-arrow.js (strict mode)
language/global-code/super-prop-arrow.js (default)
language/global-code/super-prop-arrow.js (strict mode)
language/module-code/early-export-global.js (default)
language/module-code/early-export-global.js (strict mode)
language/module-code/early-export-unresolvable.js (default)
language/module-code/early-export-unresolvable.js (strict mode)
language/statements/async-function/early-errors-declaration-body-contains-super-call.js (default)
language/statements/async-function/early-errors-declaration-body-contains-super-call.js (strict mode)
language/statements/async-function/early-errors-declaration-body-contains-super-property.js (default)
language/statements/async-function/early-errors-declaration-body-contains-super-property.js (strict mode)
language/statements/async-function/early-errors-declaration-formals-contains-super-call.js (default)
language/statements/async-function/early-errors-declaration-formals-contains-super-call.js (strict mode)
language/statements/async-function/early-errors-declaration-formals-contains-super-property.js (default)
language/statements/async-function/early-errors-declaration-formals-contains-super-property.js (strict mode)
language/expressions/async-generator/early-errors-expression-body-contains-super-call.js (default)
language/expressions/async-generator/early-errors-expression-body-contains-super-call.js (strict mode)
language/expressions/async-generator/early-errors-expression-body-contains-super-property.js (default)
language/expressions/async-generator/early-errors-expression-body-contains-super-property.js (strict mode)
language/expressions/async-generator/early-errors-expression-formals-contains-super-call.js (default)
language/expressions/async-generator/early-errors-expression-formals-contains-super-call.js (strict mode)
language/expressions/async-generator/early-errors-expression-formals-contains-super-property.js (default)
language/expressions/async-generator/early-errors-expression-formals-contains-super-property.js (strict mode)
language/statements/class/definition/early-errors-class-method-arguments-in-formal-parameters.js (default)
language/statements/class/definition/early-errors-class-method-body-contains-super-call.js (default)
language/statements/class/definition/early-errors-class-method-body-contains-super-call.js (strict mode)
language/statements/class/definition/early-errors-class-method-duplicate-parameters.js (default)
language/statements/class/definition/early-errors-class-method-eval-in-formal-parameters.js (default)
language/statements/class/definition/early-errors-class-method-formals-contains-super-call.js (default)
language/statements/class/definition/early-errors-class-method-formals-contains-super-call.js (strict mode)
language/statements/class/definition/methods-gen-yield-as-function-expression-binding-identifier.js (default)
language/statements/class/definition/methods-gen-yield-as-identifier-in-nested-function.js (default)
language/statements/class/method-param-yield.js (default)
language/statements/class/static-method-param-yield.js (default)
language/statements/class/strict-mode/with.js (default)
language/statements/class/syntax/early-errors/class-body-has-direct-super-missing-class-heritage.js (default)
language/statements/class/syntax/early-errors/class-body-has-direct-super-missing-class-heritage.js (strict mode)
language/statements/class/syntax/early-errors/class-body-method-contains-direct-super.js (default)
language/statements/class/syntax/early-errors/class-body-method-contains-direct-super.js (strict mode)
language/statements/class/syntax/early-errors/class-body-special-method-generator-contains-direct-super.js (default)
language/statements/class/syntax/early-errors/class-body-special-method-generator-contains-direct-super.js (strict mode)
language/statements/class/syntax/early-errors/class-body-special-method-get-contains-direct-super.js (default)
language/statements/class/syntax/early-errors/class-body-special-method-get-contains-direct-super.js (strict mode)
language/statements/class/syntax/early-errors/class-body-special-method-set-contains-direct-super.js (default)
language/statements/class/syntax/early-errors/class-body-special-method-set-contains-direct-super.js (strict mode)
language/statements/class/syntax/early-errors/class-body-static-method-contains-direct-super.js (default)
language/statements/class/syntax/early-errors/class-body-static-method-contains-direct-super.js (strict mode)
language/statements/class/syntax/early-errors/class-body-static-method-get-contains-direct-super.js (default)
language/statements/class/syntax/early-errors/class-body-static-method-get-contains-direct-super.js (strict mode)
language/statements/class/syntax/early-errors/class-body-static-method-set-contains-direct-super.js (default)
language/statements/class/syntax/early-errors/class-body-static-method-set-contains-direct-super.js (strict mode)
language/statements/class/syntax/early-errors/class-definition-evaluation-block-duplicate-binding.js (default)
language/statements/class/syntax/early-errors/class-definition-evaluation-block-duplicate-binding.js (strict mode)
language/statements/class/syntax/early-errors/class-definition-evaluation-scriptbody-duplicate-binding.js (default)
language/statements/class/syntax/early-errors/class-definition-evaluation-scriptbody-duplicate-binding.js (strict mode)
language/statements/const/syntax/const-declaring-let-split-across-two-lines.js (default)
language/statements/do-while/labelled-fn-stmt.js (default)
language/statements/for/head-let-bound-names-in-stmt.js (default)
language/statements/for/head-let-bound-names-in-stmt.js (strict mode)
language/statements/for-in/head-const-bound-names-in-stmt.js (default)
language/statements/for-in/head-const-bound-names-in-stmt.js (strict mode)
language/statements/for-in/head-const-bound-names-let.js (default)
language/statements/for-in/head-let-bound-names-in-stmt.js (default)
language/statements/for-in/head-let-bound-names-in-stmt.js (strict mode)
language/statements/for-in/head-let-bound-names-let.js (default)
language/statements/for-in/labelled-fn-stmt-const.js (default)
language/statements/for-in/labelled-fn-stmt-let.js (default)
language/statements/for-in/labelled-fn-stmt-lhs.js (default)
language/statements/for-in/labelled-fn-stmt-var.js (default)
language/statements/for-in/let-block-with-newline.js (default)
language/statements/for-in/let-identifier-with-newline.js (default)
language/statements/for/labelled-fn-stmt-expr.js (default)
language/statements/for/labelled-fn-stmt-let.js (default)
language/statements/for/labelled-fn-stmt-var.js (default)
language/statements/for/let-block-with-newline.js (default)
language/statements/for/let-identifier-with-newline.js (default)
language/statements/for-of/head-const-bound-names-in-stmt.js (default)
language/statements/for-of/head-const-bound-names-in-stmt.js (strict mode)
language/statements/for-of/head-const-bound-names-let.js (default)
language/statements/for-of/head-let-bound-names-in-stmt.js (default)
language/statements/for-of/head-let-bound-names-in-stmt.js (strict mode)
language/statements/for-of/head-let-bound-names-let.js (default)
language/statements/for-of/labelled-fn-stmt-const.js (default)
language/statements/for-of/labelled-fn-stmt-let.js (default)
language/statements/for-of/labelled-fn-stmt-lhs.js (default)
language/statements/for-of/labelled-fn-stmt-var.js (default)
language/statements/for-of/let-block-with-newline.js (default)
language/statements/for-of/let-identifier-with-newline.js (default)
language/statements/for-await-of/let-block-with-newline.js (default)
language/statements/for-await-of/let-identifier-with-newline.js (default)
language/statements/function/early-body-super-call.js (default)
language/statements/function/early-body-super-call.js (strict mode)
language/statements/function/early-body-super-prop.js (default)
language/statements/function/early-body-super-prop.js (strict mode)
language/statements/function/early-params-super-call.js (default)
language/statements/function/early-params-super-call.js (strict mode)
language/statements/function/early-params-super-prop.js (default)
language/statements/function/early-params-super-prop.js (strict mode)
language/statements/if/if-gen-else-gen.js (default)
language/statements/if/if-gen-else-stmt.js (default)
language/statements/if/if-gen-no-else.js (default)
language/statements/if/if-stmt-else-gen.js (default)
language/statements/if/labelled-fn-stmt-first.js (default)
language/statements/if/labelled-fn-stmt-lone.js (default)
language/statements/if/labelled-fn-stmt-second.js (default)
language/statements/if/let-block-with-newline.js (default)
language/statements/if/let-identifier-with-newline.js (default)
language/statements/labeled/let-block-with-newline.js (default)
language/statements/labeled/let-identifier-with-newline.js (default)
language/statements/let/syntax/identifier-let-disallowed-as-boundname.js (default)
language/statements/let/syntax/let-let-declaration-split-across-two-lines.js (default)
language/statements/let/syntax/let-let-declaration-with-initializer-split-across-two-lines.js (default)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-function-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/statements/switch/syntax/redeclaration/async-generator-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-async-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-async-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-const-declaration.js (default)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-const-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-let-declaration.js (default)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-let-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/statements/switch/syntax/redeclaration/class-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/const-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/statements/switch/syntax/redeclaration/const-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/statements/switch/syntax/redeclaration/function-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-var-declaration.js (default)
language/statements/switch/syntax/redeclaration/generator-declaration-attempt-to-redeclare-with-var-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/let-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/statements/switch/syntax/redeclaration/let-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-async-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-async-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-class-declaration.js (default)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-class-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-function-declaration.js (default)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-function-declaration.js (strict mode)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-generator-declaration.js (default)
language/statements/switch/syntax/redeclaration/var-declaration-attempt-to-redeclare-with-generator-declaration.js (strict mode)
language/statements/while/labelled-fn-stmt.js (default)
language/statements/while/let-block-with-newline.js (default)
language/statements/while/let-identifier-with-newline.js (default)
language/statements/with/labelled-fn-stmt.js (default)
language/statements/with/let-block-with-newline.js (default)
language/statements/with/let-identifier-with-newline.js (default)
language/white-space/mongolian-vowel-separator.js (default)
language/white-space/mongolian-vowel-separator.js (strict mode)
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+423
View File
@@ -0,0 +1,423 @@
// AST walker module for Mozilla Parser API compatible trees
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
function simple(node, visitors, baseVisitor, state, override) {
if (!baseVisitor) { baseVisitor = base
; }(function c(node, st, override) {
var type = override || node.type, found = visitors[type];
baseVisitor[type](node, st, c);
if (found) { found(node, st); }
})(node, state, override);
}
// An ancestor walk keeps an array of ancestor nodes (including the
// current node) and passes them to the callback as third parameter
// (and also as state parameter when no other state is present).
function ancestor(node, visitors, baseVisitor, state) {
var ancestors = [];
if (!baseVisitor) { baseVisitor = base
; }(function c(node, st, override) {
var type = override || node.type, found = visitors[type];
var isNew = node !== ancestors[ancestors.length - 1];
if (isNew) { ancestors.push(node); }
baseVisitor[type](node, st, c);
if (found) { found(node, st || ancestors, ancestors); }
if (isNew) { ancestors.pop(); }
})(node, state);
}
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
function recursive(node, state, funcs, baseVisitor, override) {
var visitor = funcs ? make(funcs, baseVisitor || undefined) : baseVisitor;(function c(node, st, override) {
visitor[override || node.type](node, st, c);
})(node, state, override);
}
function makeTest(test) {
if (typeof test === "string")
{ return function (type) { return type === test; } }
else if (!test)
{ return function () { return true; } }
else
{ return test }
}
var Found = function Found(node, state) { this.node = node; this.state = state; };
// A full walk triggers the callback on each node
function full(node, callback, baseVisitor, state, override) {
if (!baseVisitor) { baseVisitor = base
; }(function c(node, st, override) {
var type = override || node.type;
baseVisitor[type](node, st, c);
if (!override) { callback(node, st, type); }
})(node, state, override);
}
// An fullAncestor walk is like an ancestor walk, but triggers
// the callback on each node
function fullAncestor(node, callback, baseVisitor, state) {
if (!baseVisitor) { baseVisitor = base; }
var ancestors = [];(function c(node, st, override) {
var type = override || node.type;
var isNew = node !== ancestors[ancestors.length - 1];
if (isNew) { ancestors.push(node); }
baseVisitor[type](node, st, c);
if (!override) { callback(node, st || ancestors, ancestors, type); }
if (isNew) { ancestors.pop(); }
})(node, state);
}
// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
function findNodeAt(node, start, end, test, baseVisitor, state) {
if (!baseVisitor) { baseVisitor = base; }
test = makeTest(test);
try {
(function c(node, st, override) {
var type = override || node.type;
if ((start == null || node.start <= start) &&
(end == null || node.end >= end))
{ baseVisitor[type](node, st, c); }
if ((start == null || node.start === start) &&
(end == null || node.end === end) &&
test(type, node))
{ throw new Found(node, st) }
})(node, state);
} catch (e) {
if (e instanceof Found) { return e }
throw e
}
}
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
function findNodeAround(node, pos, test, baseVisitor, state) {
test = makeTest(test);
if (!baseVisitor) { baseVisitor = base; }
try {
(function c(node, st, override) {
var type = override || node.type;
if (node.start > pos || node.end < pos) { return }
baseVisitor[type](node, st, c);
if (test(type, node)) { throw new Found(node, st) }
})(node, state);
} catch (e) {
if (e instanceof Found) { return e }
throw e
}
}
// Find the outermost matching node after a given position.
function findNodeAfter(node, pos, test, baseVisitor, state) {
test = makeTest(test);
if (!baseVisitor) { baseVisitor = base; }
try {
(function c(node, st, override) {
if (node.end < pos) { return }
var type = override || node.type;
if (node.start >= pos && test(type, node)) { throw new Found(node, st) }
baseVisitor[type](node, st, c);
})(node, state);
} catch (e) {
if (e instanceof Found) { return e }
throw e
}
}
// Find the outermost matching node before a given position.
function findNodeBefore(node, pos, test, baseVisitor, state) {
test = makeTest(test);
if (!baseVisitor) { baseVisitor = base; }
var max;(function c(node, st, override) {
if (node.start > pos) { return }
var type = override || node.type;
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
{ max = new Found(node, st); }
baseVisitor[type](node, st, c);
})(node, state);
return max
}
// Fallback to an Object.create polyfill for older environments.
var create = Object.create || function(proto) {
function Ctor() {}
Ctor.prototype = proto;
return new Ctor
};
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
function make(funcs, baseVisitor) {
var visitor = create(baseVisitor || base);
for (var type in funcs) { visitor[type] = funcs[type]; }
return visitor
}
function skipThrough(node, st, c) { c(node, st); }
function ignore(_node, _st, _c) {}
// Node walkers.
var base = {};
base.Program = base.BlockStatement = function (node, st, c) {
for (var i = 0, list = node.body; i < list.length; i += 1)
{
var stmt = list[i];
c(stmt, st, "Statement");
}
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression =
function (node, st, c) { return c(node.expression, st, "Expression"); };
base.IfStatement = function (node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Statement");
if (node.alternate) { c(node.alternate, st, "Statement"); }
};
base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); };
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function (node, st, c) {
c(node.object, st, "Expression");
c(node.body, st, "Statement");
};
base.SwitchStatement = function (node, st, c) {
c(node.discriminant, st, "Expression");
for (var i = 0, list = node.cases; i < list.length; i += 1) {
var cs = list[i];
if (cs.test) { c(cs.test, st, "Expression"); }
for (var i$1 = 0, list$1 = cs.consequent; i$1 < list$1.length; i$1 += 1)
{
var cons = list$1[i$1];
c(cons, st, "Statement");
}
}
};
base.SwitchCase = function (node, st, c) {
if (node.test) { c(node.test, st, "Expression"); }
for (var i = 0, list = node.consequent; i < list.length; i += 1)
{
var cons = list[i];
c(cons, st, "Statement");
}
};
base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {
if (node.argument) { c(node.argument, st, "Expression"); }
};
base.ThrowStatement = base.SpreadElement =
function (node, st, c) { return c(node.argument, st, "Expression"); };
base.TryStatement = function (node, st, c) {
c(node.block, st, "Statement");
if (node.handler) { c(node.handler, st); }
if (node.finalizer) { c(node.finalizer, st, "Statement"); }
};
base.CatchClause = function (node, st, c) {
if (node.param) { c(node.param, st, "Pattern"); }
c(node.body, st, "ScopeBody");
};
base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
c(node.test, st, "Expression");
c(node.body, st, "Statement");
};
base.ForStatement = function (node, st, c) {
if (node.init) { c(node.init, st, "ForInit"); }
if (node.test) { c(node.test, st, "Expression"); }
if (node.update) { c(node.update, st, "Expression"); }
c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function (node, st, c) {
c(node.left, st, "ForInit");
c(node.right, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInit = function (node, st, c) {
if (node.type === "VariableDeclaration") { c(node, st); }
else { c(node, st, "Expression"); }
};
base.DebuggerStatement = ignore;
base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); };
base.VariableDeclaration = function (node, st, c) {
for (var i = 0, list = node.declarations; i < list.length; i += 1)
{
var decl = list[i];
c(decl, st);
}
};
base.VariableDeclarator = function (node, st, c) {
c(node.id, st, "Pattern");
if (node.init) { c(node.init, st, "Expression"); }
};
base.Function = function (node, st, c) {
if (node.id) { c(node.id, st, "Pattern"); }
for (var i = 0, list = node.params; i < list.length; i += 1)
{
var param = list[i];
c(param, st, "Pattern");
}
c(node.body, st, node.expression ? "ScopeExpression" : "ScopeBody");
};
// FIXME drop these node types in next major version
// (They are awkward, and in ES6 every block can be a scope.)
base.ScopeBody = function (node, st, c) { return c(node, st, "Statement"); };
base.ScopeExpression = function (node, st, c) { return c(node, st, "Expression"); };
base.Pattern = function (node, st, c) {
if (node.type === "Identifier")
{ c(node, st, "VariablePattern"); }
else if (node.type === "MemberExpression")
{ c(node, st, "MemberPattern"); }
else
{ c(node, st); }
};
base.VariablePattern = ignore;
base.MemberPattern = skipThrough;
base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); };
base.ArrayPattern = function (node, st, c) {
for (var i = 0, list = node.elements; i < list.length; i += 1) {
var elt = list[i];
if (elt) { c(elt, st, "Pattern"); }
}
};
base.ObjectPattern = function (node, st, c) {
for (var i = 0, list = node.properties; i < list.length; i += 1) {
var prop = list[i];
if (prop.type === "Property") {
if (prop.computed) { c(prop.key, st, "Expression"); }
c(prop.value, st, "Pattern");
} else if (prop.type === "RestElement") {
c(prop.argument, st, "Pattern");
}
}
};
base.Expression = skipThrough;
base.ThisExpression = base.Super = base.MetaProperty = ignore;
base.ArrayExpression = function (node, st, c) {
for (var i = 0, list = node.elements; i < list.length; i += 1) {
var elt = list[i];
if (elt) { c(elt, st, "Expression"); }
}
};
base.ObjectExpression = function (node, st, c) {
for (var i = 0, list = node.properties; i < list.length; i += 1)
{
var prop = list[i];
c(prop, st);
}
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = base.TemplateLiteral = function (node, st, c) {
for (var i = 0, list = node.expressions; i < list.length; i += 1)
{
var expr = list[i];
c(expr, st, "Expression");
}
};
base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
c(node.argument, st, "Expression");
};
base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
c(node.left, st, "Expression");
c(node.right, st, "Expression");
};
base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
c(node.left, st, "Pattern");
c(node.right, st, "Expression");
};
base.ConditionalExpression = function (node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Expression");
c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function (node, st, c) {
c(node.callee, st, "Expression");
if (node.arguments)
{ for (var i = 0, list = node.arguments; i < list.length; i += 1)
{
var arg = list[i];
c(arg, st, "Expression");
} }
};
base.MemberExpression = function (node, st, c) {
c(node.object, st, "Expression");
if (node.computed) { c(node.property, st, "Expression"); }
};
base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
if (node.declaration)
{ c(node.declaration, st, node.type === "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); }
if (node.source) { c(node.source, st, "Expression"); }
};
base.ExportAllDeclaration = function (node, st, c) {
c(node.source, st, "Expression");
};
base.ImportDeclaration = function (node, st, c) {
for (var i = 0, list = node.specifiers; i < list.length; i += 1)
{
var spec = list[i];
c(spec, st);
}
c(node.source, st, "Expression");
};
base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.Literal = ignore;
base.TaggedTemplateExpression = function (node, st, c) {
c(node.tag, st, "Expression");
c(node.quasi, st, "Expression");
};
base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); };
base.Class = function (node, st, c) {
if (node.id) { c(node.id, st, "Pattern"); }
if (node.superClass) { c(node.superClass, st, "Expression"); }
c(node.body, st);
};
base.ClassBody = function (node, st, c) {
for (var i = 0, list = node.body; i < list.length; i += 1)
{
var elt = list[i];
c(elt, st);
}
};
base.MethodDefinition = base.Property = function (node, st, c) {
if (node.computed) { c(node.key, st, "Expression"); }
c(node.value, st, "Expression");
};
export { simple, ancestor, recursive, full, fullAncestor, findNodeAt, findNodeAround, findNodeAfter, findNodeBefore, make, base };
+443
View File
@@ -0,0 +1,443 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.acorn = global.acorn || {}, global.acorn.walk = {})));
}(this, (function (exports) { 'use strict';
// AST walker module for Mozilla Parser API compatible trees
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
function simple(node, visitors, baseVisitor, state, override) {
if (!baseVisitor) { baseVisitor = base
; }(function c(node, st, override) {
var type = override || node.type, found = visitors[type];
baseVisitor[type](node, st, c);
if (found) { found(node, st); }
})(node, state, override);
}
// An ancestor walk keeps an array of ancestor nodes (including the
// current node) and passes them to the callback as third parameter
// (and also as state parameter when no other state is present).
function ancestor(node, visitors, baseVisitor, state) {
var ancestors = [];
if (!baseVisitor) { baseVisitor = base
; }(function c(node, st, override) {
var type = override || node.type, found = visitors[type];
var isNew = node !== ancestors[ancestors.length - 1];
if (isNew) { ancestors.push(node); }
baseVisitor[type](node, st, c);
if (found) { found(node, st || ancestors, ancestors); }
if (isNew) { ancestors.pop(); }
})(node, state);
}
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
function recursive(node, state, funcs, baseVisitor, override) {
var visitor = funcs ? make(funcs, baseVisitor || undefined) : baseVisitor;(function c(node, st, override) {
visitor[override || node.type](node, st, c);
})(node, state, override);
}
function makeTest(test) {
if (typeof test === "string")
{ return function (type) { return type === test; } }
else if (!test)
{ return function () { return true; } }
else
{ return test }
}
var Found = function Found(node, state) { this.node = node; this.state = state; };
// A full walk triggers the callback on each node
function full(node, callback, baseVisitor, state, override) {
if (!baseVisitor) { baseVisitor = base
; }(function c(node, st, override) {
var type = override || node.type;
baseVisitor[type](node, st, c);
if (!override) { callback(node, st, type); }
})(node, state, override);
}
// An fullAncestor walk is like an ancestor walk, but triggers
// the callback on each node
function fullAncestor(node, callback, baseVisitor, state) {
if (!baseVisitor) { baseVisitor = base; }
var ancestors = [];(function c(node, st, override) {
var type = override || node.type;
var isNew = node !== ancestors[ancestors.length - 1];
if (isNew) { ancestors.push(node); }
baseVisitor[type](node, st, c);
if (!override) { callback(node, st || ancestors, ancestors, type); }
if (isNew) { ancestors.pop(); }
})(node, state);
}
// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
function findNodeAt(node, start, end, test, baseVisitor, state) {
if (!baseVisitor) { baseVisitor = base; }
test = makeTest(test);
try {
(function c(node, st, override) {
var type = override || node.type;
if ((start == null || node.start <= start) &&
(end == null || node.end >= end))
{ baseVisitor[type](node, st, c); }
if ((start == null || node.start === start) &&
(end == null || node.end === end) &&
test(type, node))
{ throw new Found(node, st) }
})(node, state);
} catch (e) {
if (e instanceof Found) { return e }
throw e
}
}
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
function findNodeAround(node, pos, test, baseVisitor, state) {
test = makeTest(test);
if (!baseVisitor) { baseVisitor = base; }
try {
(function c(node, st, override) {
var type = override || node.type;
if (node.start > pos || node.end < pos) { return }
baseVisitor[type](node, st, c);
if (test(type, node)) { throw new Found(node, st) }
})(node, state);
} catch (e) {
if (e instanceof Found) { return e }
throw e
}
}
// Find the outermost matching node after a given position.
function findNodeAfter(node, pos, test, baseVisitor, state) {
test = makeTest(test);
if (!baseVisitor) { baseVisitor = base; }
try {
(function c(node, st, override) {
if (node.end < pos) { return }
var type = override || node.type;
if (node.start >= pos && test(type, node)) { throw new Found(node, st) }
baseVisitor[type](node, st, c);
})(node, state);
} catch (e) {
if (e instanceof Found) { return e }
throw e
}
}
// Find the outermost matching node before a given position.
function findNodeBefore(node, pos, test, baseVisitor, state) {
test = makeTest(test);
if (!baseVisitor) { baseVisitor = base; }
var max;(function c(node, st, override) {
if (node.start > pos) { return }
var type = override || node.type;
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
{ max = new Found(node, st); }
baseVisitor[type](node, st, c);
})(node, state);
return max
}
// Fallback to an Object.create polyfill for older environments.
var create = Object.create || function(proto) {
function Ctor() {}
Ctor.prototype = proto;
return new Ctor
};
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
function make(funcs, baseVisitor) {
var visitor = create(baseVisitor || base);
for (var type in funcs) { visitor[type] = funcs[type]; }
return visitor
}
function skipThrough(node, st, c) { c(node, st); }
function ignore(_node, _st, _c) {}
// Node walkers.
var base = {};
base.Program = base.BlockStatement = function (node, st, c) {
for (var i = 0, list = node.body; i < list.length; i += 1)
{
var stmt = list[i];
c(stmt, st, "Statement");
}
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression =
function (node, st, c) { return c(node.expression, st, "Expression"); };
base.IfStatement = function (node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Statement");
if (node.alternate) { c(node.alternate, st, "Statement"); }
};
base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); };
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function (node, st, c) {
c(node.object, st, "Expression");
c(node.body, st, "Statement");
};
base.SwitchStatement = function (node, st, c) {
c(node.discriminant, st, "Expression");
for (var i = 0, list = node.cases; i < list.length; i += 1) {
var cs = list[i];
if (cs.test) { c(cs.test, st, "Expression"); }
for (var i$1 = 0, list$1 = cs.consequent; i$1 < list$1.length; i$1 += 1)
{
var cons = list$1[i$1];
c(cons, st, "Statement");
}
}
};
base.SwitchCase = function (node, st, c) {
if (node.test) { c(node.test, st, "Expression"); }
for (var i = 0, list = node.consequent; i < list.length; i += 1)
{
var cons = list[i];
c(cons, st, "Statement");
}
};
base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {
if (node.argument) { c(node.argument, st, "Expression"); }
};
base.ThrowStatement = base.SpreadElement =
function (node, st, c) { return c(node.argument, st, "Expression"); };
base.TryStatement = function (node, st, c) {
c(node.block, st, "Statement");
if (node.handler) { c(node.handler, st); }
if (node.finalizer) { c(node.finalizer, st, "Statement"); }
};
base.CatchClause = function (node, st, c) {
if (node.param) { c(node.param, st, "Pattern"); }
c(node.body, st, "ScopeBody");
};
base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
c(node.test, st, "Expression");
c(node.body, st, "Statement");
};
base.ForStatement = function (node, st, c) {
if (node.init) { c(node.init, st, "ForInit"); }
if (node.test) { c(node.test, st, "Expression"); }
if (node.update) { c(node.update, st, "Expression"); }
c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function (node, st, c) {
c(node.left, st, "ForInit");
c(node.right, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInit = function (node, st, c) {
if (node.type === "VariableDeclaration") { c(node, st); }
else { c(node, st, "Expression"); }
};
base.DebuggerStatement = ignore;
base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); };
base.VariableDeclaration = function (node, st, c) {
for (var i = 0, list = node.declarations; i < list.length; i += 1)
{
var decl = list[i];
c(decl, st);
}
};
base.VariableDeclarator = function (node, st, c) {
c(node.id, st, "Pattern");
if (node.init) { c(node.init, st, "Expression"); }
};
base.Function = function (node, st, c) {
if (node.id) { c(node.id, st, "Pattern"); }
for (var i = 0, list = node.params; i < list.length; i += 1)
{
var param = list[i];
c(param, st, "Pattern");
}
c(node.body, st, node.expression ? "ScopeExpression" : "ScopeBody");
};
// FIXME drop these node types in next major version
// (They are awkward, and in ES6 every block can be a scope.)
base.ScopeBody = function (node, st, c) { return c(node, st, "Statement"); };
base.ScopeExpression = function (node, st, c) { return c(node, st, "Expression"); };
base.Pattern = function (node, st, c) {
if (node.type === "Identifier")
{ c(node, st, "VariablePattern"); }
else if (node.type === "MemberExpression")
{ c(node, st, "MemberPattern"); }
else
{ c(node, st); }
};
base.VariablePattern = ignore;
base.MemberPattern = skipThrough;
base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); };
base.ArrayPattern = function (node, st, c) {
for (var i = 0, list = node.elements; i < list.length; i += 1) {
var elt = list[i];
if (elt) { c(elt, st, "Pattern"); }
}
};
base.ObjectPattern = function (node, st, c) {
for (var i = 0, list = node.properties; i < list.length; i += 1) {
var prop = list[i];
if (prop.type === "Property") {
if (prop.computed) { c(prop.key, st, "Expression"); }
c(prop.value, st, "Pattern");
} else if (prop.type === "RestElement") {
c(prop.argument, st, "Pattern");
}
}
};
base.Expression = skipThrough;
base.ThisExpression = base.Super = base.MetaProperty = ignore;
base.ArrayExpression = function (node, st, c) {
for (var i = 0, list = node.elements; i < list.length; i += 1) {
var elt = list[i];
if (elt) { c(elt, st, "Expression"); }
}
};
base.ObjectExpression = function (node, st, c) {
for (var i = 0, list = node.properties; i < list.length; i += 1)
{
var prop = list[i];
c(prop, st);
}
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = base.TemplateLiteral = function (node, st, c) {
for (var i = 0, list = node.expressions; i < list.length; i += 1)
{
var expr = list[i];
c(expr, st, "Expression");
}
};
base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
c(node.argument, st, "Expression");
};
base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
c(node.left, st, "Expression");
c(node.right, st, "Expression");
};
base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
c(node.left, st, "Pattern");
c(node.right, st, "Expression");
};
base.ConditionalExpression = function (node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Expression");
c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function (node, st, c) {
c(node.callee, st, "Expression");
if (node.arguments)
{ for (var i = 0, list = node.arguments; i < list.length; i += 1)
{
var arg = list[i];
c(arg, st, "Expression");
} }
};
base.MemberExpression = function (node, st, c) {
c(node.object, st, "Expression");
if (node.computed) { c(node.property, st, "Expression"); }
};
base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
if (node.declaration)
{ c(node.declaration, st, node.type === "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); }
if (node.source) { c(node.source, st, "Expression"); }
};
base.ExportAllDeclaration = function (node, st, c) {
c(node.source, st, "Expression");
};
base.ImportDeclaration = function (node, st, c) {
for (var i = 0, list = node.specifiers; i < list.length; i += 1)
{
var spec = list[i];
c(spec, st);
}
c(node.source, st, "Expression");
};
base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.Literal = ignore;
base.TaggedTemplateExpression = function (node, st, c) {
c(node.tag, st, "Expression");
c(node.quasi, st, "Expression");
};
base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); };
base.Class = function (node, st, c) {
if (node.id) { c(node.id, st, "Pattern"); }
if (node.superClass) { c(node.superClass, st, "Expression"); }
c(node.body, st);
};
base.ClassBody = function (node, st, c) {
for (var i = 0, list = node.body; i < list.length; i += 1)
{
var elt = list[i];
c(elt, st);
}
};
base.MethodDefinition = base.Property = function (node, st, c) {
if (node.computed) { c(node.key, st, "Expression"); }
c(node.value, st, "Expression");
};
exports.simple = simple;
exports.ancestor = ancestor;
exports.recursive = recursive;
exports.full = full;
exports.fullAncestor = fullAncestor;
exports.findNodeAt = findNodeAt;
exports.findNodeAround = findNodeAround;
exports.findNodeAfter = findNodeAfter;
exports.findNodeBefore = findNodeBefore;
exports.make = make;
exports.base = base;
Object.defineProperty(exports, '__esModule', { value: true });
})));
+324
View File
@@ -0,0 +1,324 @@
{
"_from": "acorn@^5.2.1",
"_id": "acorn@5.7.3",
"_inBundle": false,
"_integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
"_location": "/commoner/acorn",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "acorn@^5.2.1",
"name": "acorn",
"escapedName": "acorn",
"rawSpec": "^5.2.1",
"saveSpec": null,
"fetchSpec": "^5.2.1"
},
"_requiredBy": [
"/commoner/detective"
],
"_resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
"_shasum": "67aa231bf8812974b85235a96771eb6bd07ea279",
"_spec": "acorn@^5.2.1",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/commoner/node_modules/detective",
"bin": {
"acorn": "./bin/acorn"
},
"bugs": {
"url": "https://github.com/acornjs/acorn/issues"
},
"bundleDependencies": false,
"contributors": [
{
"name": "List of Acorn contributors. Updated before every release."
},
{
"name": "Adrian Heine"
},
{
"name": "Adrian Rakovsky"
},
{
"name": "Alistair Braidwood"
},
{
"name": "Amila Welihinda"
},
{
"name": "Andres Suarez"
},
{
"name": "Angelo"
},
{
"name": "Aparajita Fishman"
},
{
"name": "Arian Stolwijk"
},
{
"name": "Artem Govorov"
},
{
"name": "Boopesh Mahendran"
},
{
"name": "Bradley Heinz"
},
{
"name": "Brandon Mills"
},
{
"name": "Charles Hughes"
},
{
"name": "Charmander"
},
{
"name": "Chris McKnight"
},
{
"name": "Conrad Irwin"
},
{
"name": "Daniel Tschinder"
},
{
"name": "David Bonnet"
},
{
"name": "Domenico Matteo"
},
{
"name": "ehmicky"
},
{
"name": "Eugene Obrezkov"
},
{
"name": "Felix Maier"
},
{
"name": "Forbes Lindesay"
},
{
"name": "Gilad Peleg"
},
{
"name": "impinball"
},
{
"name": "Ingvar Stepanyan"
},
{
"name": "Jackson Ray Hamilton"
},
{
"name": "Jesse McCarthy"
},
{
"name": "Jiaxing Wang"
},
{
"name": "Joel Kemp"
},
{
"name": "Johannes Herr"
},
{
"name": "John-David Dalton"
},
{
"name": "Jordan Klassen"
},
{
"name": "Jürg Lehni"
},
{
"name": "Kai Cataldo"
},
{
"name": "keeyipchan"
},
{
"name": "Keheliya Gallaba"
},
{
"name": "Kevin Irish"
},
{
"name": "Kevin Kwok"
},
{
"name": "krator"
},
{
"name": "laosb"
},
{
"name": "luckyzeng"
},
{
"name": "Marek"
},
{
"name": "Marijn Haverbeke"
},
{
"name": "Martin Carlberg"
},
{
"name": "Mat Garcia"
},
{
"name": "Mathias Bynens"
},
{
"name": "Mathieu 'p01' Henri"
},
{
"name": "Matthew Bastien"
},
{
"name": "Max Schaefer"
},
{
"name": "Max Zerzouri"
},
{
"name": "Mihai Bazon"
},
{
"name": "Mike Rennie"
},
{
"name": "naoh"
},
{
"name": "Nicholas C. Zakas"
},
{
"name": "Nick Fitzgerald"
},
{
"name": "Olivier Thomann"
},
{
"name": "Oskar Schöldström"
},
{
"name": "Paul Harper"
},
{
"name": "Peter Rust"
},
{
"name": "PlNG"
},
{
"name": "Prayag Verma"
},
{
"name": "ReadmeCritic"
},
{
"name": "r-e-d"
},
{
"name": "Renée Kooi"
},
{
"name": "Richard Gibson"
},
{
"name": "Rich Harris"
},
{
"name": "Sebastian McKenzie"
},
{
"name": "Shahar Soel"
},
{
"name": "Sheel Bedi"
},
{
"name": "Simen Bekkhus"
},
{
"name": "Teddy Katz"
},
{
"name": "Timothy Gu"
},
{
"name": "Toru Nagashima"
},
{
"name": "Victor Homyakov"
},
{
"name": "Wexpo Lyu"
},
{
"name": "zsjforcn"
}
],
"deprecated": false,
"description": "ECMAScript parser",
"devDependencies": {
"eslint": "^4.10.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^3.0.1",
"rollup": "^0.45.0",
"rollup-plugin-buble": "^0.16.0",
"test262": "git+https://github.com/tc39/test262.git#3bfad28cc302fd4455badcfcbca7c5bb7ce41a72",
"test262-parser-runner": "^0.4.0",
"unicode-11.0.0": "^0.7.7"
},
"engines": {
"node": ">=0.4.0"
},
"homepage": "https://github.com/acornjs/acorn",
"license": "MIT",
"main": "dist/acorn.js",
"maintainers": [
{
"name": "Marijn Haverbeke",
"email": "marijnh@gmail.com",
"url": "http://marijnhaverbeke.nl"
},
{
"name": "Ingvar Stepanyan",
"email": "me@rreverser.com",
"url": "http://rreverser.com/"
},
{
"name": "Adrian Heine",
"email": "http://adrianheine.de"
}
],
"module": "dist/acorn.es.js",
"name": "acorn",
"repository": {
"type": "git",
"url": "git+https://github.com/acornjs/acorn.git"
},
"scripts": {
"build": "npm run build:main && npm run build:walk && npm run build:loose && npm run build:bin",
"build:bin": "rollup -c rollup/config.bin.js",
"build:loose": "rollup -c rollup/config.loose.js && rollup -c rollup/config.loose_es.js",
"build:main": "rollup -c rollup/config.main.js",
"build:walk": "rollup -c rollup/config.walk.js",
"lint": "eslint src/",
"prepare": "npm run build && node test/run.js && node test/lint.js",
"pretest": "npm run build:main && npm run build:loose",
"test": "node test/run.js && node test/lint.js",
"test:test262": "node bin/run_test262.js"
},
"version": "5.7.3"
}
+7
View File
@@ -0,0 +1,7 @@
language: node_js
node_js:
- 9
- 8
- 6
- 4
- "0.12"
+18
View File
@@ -0,0 +1,18 @@
This software is released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+7
View File
@@ -0,0 +1,7 @@
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/src/jquery.js', 'utf8');
var t0 = Date.now();
var requires = detective(src);
console.log(Date.now() - t0);
+18
View File
@@ -0,0 +1,18 @@
esprima:
$ for i in {1..5}; do node detect.js; done
704
702
704
704
697
acorn:
$ for i in {1..5}; do node detect.js; done
555
552
585
549
583
+6
View File
@@ -0,0 +1,6 @@
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/strings_src.js');
var requires = detective(src);
console.dir(requires);
+3
View File
@@ -0,0 +1,3 @@
var a = require('a');
var b = require('b');
var c = require('c');
+80
View File
@@ -0,0 +1,80 @@
var acorn = require('acorn');
var walk = require('acorn/dist/walk');
var defined = require('defined');
var requireRe = /\brequire\b/;
function parse (src, opts) {
if (!opts) opts = {};
return acorn.parse(src, {
ecmaVersion: defined(opts.ecmaVersion, 9),
sourceType: opts.sourceType,
ranges: defined(opts.ranges, opts.range),
locations: defined(opts.locations, opts.loc),
allowReserved: defined(opts.allowReserved, true),
allowReturnOutsideFunction: defined(
opts.allowReturnOutsideFunction, true
),
allowImportExportEverywhere: defined(
opts.allowImportExportEverywhere, true
),
allowHashBang: defined(opts.allowHashBang, true)
});
}
var exports = module.exports = function (src, opts) {
return exports.find(src, opts).strings;
};
exports.find = function (src, opts) {
if (!opts) opts = {};
var word = opts.word === undefined ? 'require' : opts.word;
if (typeof src !== 'string') src = String(src);
var isRequire = opts.isRequire || function (node) {
return node.callee.type === 'Identifier'
&& node.callee.name === word
;
};
var modules = { strings : [], expressions : [] };
if (opts.nodes) modules.nodes = [];
var wordRe = word === 'require' ? requireRe : RegExp('\\b' + word + '\\b');
if (!wordRe.test(src)) return modules;
var ast = parse(src, opts.parse);
function visit(node, st, c) {
var hasRequire = wordRe.test(src.slice(node.start, node.end));
if (!hasRequire) return;
walk.base[node.type](node, st, c);
if (node.type !== 'CallExpression') return;
if (isRequire(node)) {
if (node.arguments.length) {
var arg = node.arguments[0];
if (arg.type === 'Literal') {
modules.strings.push(arg.value);
}
else if (arg.type === 'TemplateLiteral'
&& arg.quasis.length === 1
&& arg.expressions.length === 0) {
modules.strings.push(arg.quasis[0].value.raw);
}
else {
modules.expressions.push(src.slice(arg.start, arg.end));
}
}
if (opts.nodes) modules.nodes.push(node);
}
}
walk.recursive(ast, null, {
Statement: visit,
Expression: visit
});
return modules;
};
+61
View File
@@ -0,0 +1,61 @@
{
"_from": "detective@^4.3.1",
"_id": "detective@4.7.1",
"_inBundle": false,
"_integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==",
"_location": "/commoner/detective",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "detective@^4.3.1",
"name": "detective",
"escapedName": "detective",
"rawSpec": "^4.3.1",
"saveSpec": null,
"fetchSpec": "^4.3.1"
},
"_requiredBy": [
"/commoner"
],
"_resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz",
"_shasum": "0eca7314338442febb6d65da54c10bb1c82b246e",
"_spec": "detective@^4.3.1",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/commoner",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"bugs": {
"url": "https://github.com/browserify/detective/issues"
},
"bundleDependencies": false,
"dependencies": {
"acorn": "^5.2.1",
"defined": "^1.0.0"
},
"deprecated": false,
"description": "find all require() calls by walking the AST",
"devDependencies": {
"tap": "^10.7.3"
},
"homepage": "https://github.com/browserify/detective#readme",
"keywords": [
"require",
"source",
"analyze",
"ast"
],
"license": "MIT",
"main": "index.js",
"name": "detective",
"repository": {
"type": "git",
"url": "git://github.com/browserify/detective.git"
},
"scripts": {
"test": "tap test/*.js"
},
"version": "4.7.1"
}
+81
View File
@@ -0,0 +1,81 @@
# detective
find all calls to `require()` by walking the AST
[![build status](https://secure.travis-ci.org/browserify/detective.png)](http://travis-ci.org/browserify/detective)
# example
## strings
strings_src.js:
``` js
var a = require('a');
var b = require('b');
var c = require('c');
```
strings.js:
``` js
var detective = require('detective');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/strings_src.js');
var requires = detective(src);
console.dir(requires);
```
output:
```
$ node examples/strings.js
[ 'a', 'b', 'c' ]
```
# methods
``` js
var detective = require('detective');
```
## detective(src, opts)
Give some source body `src`, return an array of all the `require()` calls with
string arguments.
The options parameter `opts` is passed along to `detective.find()`.
## var found = detective.find(src, opts)
Give some source body `src`, return `found` with:
* `found.strings` - an array of each string found in a `require()`
* `found.expressions` - an array of each stringified expression found in a
`require()` call
* `found.nodes` (when `opts.nodes === true`) - an array of AST nodes for each
argument found in a `require()` call
Optionally:
* `opts.word` - specify a different function name instead of `"require"`
* `opts.nodes` - when `true`, populate `found.nodes`
* `opts.isRequire(node)` - a function returning whether an AST `CallExpression`
node is a require call
* `opts.parse` - supply options directly to
[acorn](https://npmjs.org/package/acorn) with some support for esprima-style
options `range` and `loc`
* `opts.ecmaVersion` - default: 9
# install
With [npm](https://npmjs.org) do:
```
npm install detective
```
# license
MIT
+26
View File
@@ -0,0 +1,26 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/both.js');
test('both', function (t) {
var modules = detective.find(src);
t.deepEqual(modules.strings, [ 'a', 'b' ]);
t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]);
t.notOk(modules.nodes, 'has no nodes');
t.end();
});
test('both with nodes specified in opts', function (t) {
var modules = detective.find(src, { nodes: true });
t.deepEqual(modules.strings, [ 'a', 'b' ]);
t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]);
t.deepEqual(
modules.nodes.map(function (n) {
var arg = n.arguments[0];
return arg.value || arg.left.value;
}),
[ 'a', 'b', 'c', 'd' ],
'has a node for each require');
t.end();
});
+9
View File
@@ -0,0 +1,9 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/chained.js');
test('chained', function (t) {
t.deepEqual(detective(src), [ 'c', 'b', 'a' ]);
t.end();
});
+58
View File
@@ -0,0 +1,58 @@
var test = require('tap').test;
var detective = require('../');
var sources = [
'require("a")',
"require('a')",
'require(`a`)',
';require("a")',
' require("a")',
'void require("a")',
'+require("a")',
'!require("a")',
'/*comments*/require("a")',
'(require("a"))',
'require/*comments*/("a")',
';require/*comments*/("a")',
' require/*comments*/("a")',
'void require/*comments*/("a")',
'+require/*comments*/("a")',
'!require/*comments*/("a")',
'/*comments*/require/*comments*/("a")',
'(require/*comments*/("a"))',
'require /*comments*/ ("a")',
';require /*comments*/ ("a")',
' require /*comments*/ ("a")',
'void require /*comments*/ ("a")',
'+require /*comments*/ ("a")',
'!require /*comments*/ ("a")',
' /*comments*/ require /*comments*/ ("a")',
'(require /*comments*/ ("a"))',
'require /*comments*/ /*more comments*/ ("a")',
';require /*comments*/ /*more comments*/ ("a")',
' require /*comments*/ /*more comments*/ ("a")',
'void require /*comments*/ /*more comments*/ ("a")',
'+require /*comments*/ /*more comments*/ ("a")',
'!require /*comments*/ /*more comments*/ ("a")',
' /*comments*/ /*more comments*/ require /*comments*/ /*more comments*/ ("a")',
'(require /*comments*/ /*more comments*/ ("a"))',
'require//comments\n("a")',
';require//comments\n("a")',
' require//comments\n("a")',
'void require//comments\n("a")',
'+require//comments\n("a")',
'!require//comments\n("a")',
' require//comments\n("a")',
'(require//comments\n("a"))'
];
test('complicated', function (t) {
t.plan(sources.length);
sources.forEach(function(src) {
t.deepEqual(detective(src), [ 'a' ]);
});
});
+9
View File
@@ -0,0 +1,9 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/es6-module.js');
test('es6-module', function (t) {
t.plan(1);
t.deepEqual(detective(src, {parse: {sourceType: 'module'}}), [ 'a', 'b' ]);
});
+4
View File
@@ -0,0 +1,4 @@
require('a');
require('b');
require('c' + x);
var moo = require('d' + y).moo;
+5
View File
@@ -0,0 +1,5 @@
require('c').hello().goodbye()
require('b').hello()
require('a')
+5
View File
@@ -0,0 +1,5 @@
var a = require('a');
export default function () {
var b = require('b');
}
+5
View File
@@ -0,0 +1,5 @@
var a = require('a');
function *gen() {
yield require('b');
}
+14
View File
@@ -0,0 +1,14 @@
var a = require.async('a');
var b = require.async('b');
var c = require.async('c');
var abc = a.b(c);
var EventEmitter = require.async('events').EventEmitter;
var x = require.async('doom')(5,6,7);
x(8,9);
c.load('notthis');
var y = require.async('y') * 100;
var EventEmitter2 = require.async('events2').EventEmitter();
+22
View File
@@ -0,0 +1,22 @@
if (true) {
(function () {
require('a');
})();
}
if (false) {
(function () {
var x = 10;
switch (x) {
case 1 : require('b'); break;
default : break;
}
})()
}
function qqq () {
require
(
"c"
);
}
@@ -0,0 +1,10 @@
var a = load('a');
var b = load('b');
var c = load('c');
var abc = a.b(c);
function load2({set = 'hello'}) {
return load('tt');
}
var loadUse = load2();
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env node
var a = require('a');
var b = require('b');
var c = require('c');
+3
View File
@@ -0,0 +1,3 @@
var o = [,,,,]
require('./foo')
+13
View File
@@ -0,0 +1,13 @@
var a = require('a');
var b = require('b');
var c = require('c');
var abc = a.b(c);
var EventEmitter = require('events').EventEmitter;
var x = require('doom')(5,6,7);
x(8,9);
c.require('notthis');
var y = require('y') * 100;
var EventEmitter2 = require('events2').EventEmitter();
+13
View File
@@ -0,0 +1,13 @@
var a = load('a');
var b = load('b');
var c = load('c');
var abc = a.b(c);
var EventEmitter = load('events').EventEmitter;
var x = load('doom')(5,6,7);
x(8,9);
c.load('notthis');
var y = load('y') * 100;
var EventEmitter2 = load('events2').EventEmitter();
+4
View File
@@ -0,0 +1,4 @@
(function * () {
var a = require('a');
var b = yield require('c')(a);
})();
+9
View File
@@ -0,0 +1,9 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/generators.js');
test('generators', function (t) {
t.plan(1);
t.deepEqual(detective(src), [ 'a', 'b' ]);
});
+20
View File
@@ -0,0 +1,20 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/isrequire.js');
test('word', function (t) {
t.deepEqual(
detective(src, { isRequire: function(node) {
return (node.type === 'CallExpression' &&
node.callee.type === 'MemberExpression' &&
node.callee.object.type == 'Identifier' &&
node.callee.object.name == 'require' &&
node.callee.property.type == 'Identifier' &&
node.callee.property.name == 'async')
} }),
[ 'a', 'b', 'c', 'events', 'doom', 'y', 'events2' ]
);
t.end();
});
+9
View File
@@ -0,0 +1,9 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/nested.js');
test('nested', function (t) {
t.deepEqual(detective(src), [ 'a', 'b', 'c' ]);
t.end();
});
+26
View File
@@ -0,0 +1,26 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
// in order to use detective to find any function
// it needs to properly handle functions called without args
var src = [ 'fn();', 'otherfn();', 'fn();' ].join('\n')
test('noargs', function (t) {
t.plan(1);
t.deepEqual(detective(src, { word: 'fn' }).length, 0, 'finds no arg id');
});
test('find noargs with nodes', function (t) {
t.plan(4);
var modules = detective.find(src, { word: 'fn', nodes: true });
t.equal(modules.strings.length, 0, 'finds no arg id');
t.equal(modules.expressions.length, 0, 'finds no expressions');
t.equal(modules.nodes.length, 2, 'finds a node for each matching function call');
t.equal(
modules.nodes.filter(function (x) {
return x.callee.name === 'fn'
}).length, 2,
'all matches are correct'
);
});
+62
View File
@@ -0,0 +1,62 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/both.js');
test('nodes specified in opts and parseopts { range: true }', function (t) {
var modules = detective.find(src, { nodes: true, parse: { range: true } });
t.deepEqual(modules.strings, [ 'a', 'b' ]);
t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]);
t.deepEqual(
modules.nodes.map(function (n) {
var arg = n.arguments[0];
return arg.value || arg.left.value;
}),
[ 'a', 'b', 'c', 'd' ],
'has a node for each require');
var range = modules.nodes[0].range;
t.equal(range[0], 0, 'includes range start');
t.equal(range[1], 12, 'includes range end');
t.end();
});
test('nodes specified in opts and parseopts { range: false }', function (t) {
var modules = detective.find(src, { nodes: true, parse: { range: false } });
t.deepEqual(modules.strings, [ 'a', 'b' ]);
t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]);
t.deepEqual(
modules.nodes.map(function (n) {
var arg = n.arguments[0];
return arg.value || arg.left.value;
}),
[ 'a', 'b', 'c', 'd' ],
'has a node for each require');
t.notOk(modules.nodes[0].range, 'includes no ranges');
t.end();
});
test('nodes specified in opts and parseopts { range: true, loc: true }', function (t) {
var modules = detective.find(src, { nodes: true, parse: { range: true, loc: true } });
t.deepEqual(modules.strings, [ 'a', 'b' ]);
t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]);
t.deepEqual(
modules.nodes.map(function (n) {
var arg = n.arguments[0];
return arg.value || arg.left.value;
}),
[ 'a', 'b', 'c', 'd' ],
'has a node for each require');
var range = modules.nodes[0].range;
t.equal(range[0], 0, 'includes range start');
t.equal(range[1], 12, 'includes range end');
var loc = modules.nodes[0].loc;
t.equal(loc.start.line, 1, 'includes start line');
t.equal(loc.start.column, 0, 'includes start column');
t.equal(loc.end.line, 1, 'includes end line');
t.equal(loc.end.column, 12, 'includes end column');
t.end();
});
+9
View File
@@ -0,0 +1,9 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = [ 'require("a")\nreturn' ];
test('return', function (t) {
t.plan(1);
t.deepEqual(detective(src), [ 'a' ]);
});
@@ -0,0 +1,12 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/set-in-object-pattern.js');
test('set in object pattern', function (t) {
t.deepEqual(
detective(src, { word : 'load' }),
[ 'a', 'b', 'c', 'tt' ]
);
t.end();
});
+9
View File
@@ -0,0 +1,9 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/shebang.js');
test('shebang', function (t) {
t.plan(1);
t.deepEqual(detective(src), [ 'a', 'b', 'c' ]);
});
+14
View File
@@ -0,0 +1,14 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/sparse-array.js');
test('sparse-array', function (t) {
//just check that this does not crash.
t.doesNotThrow(function () {
detective(src)
})
t.end();
});
+9
View File
@@ -0,0 +1,9 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/strings.js');
test('single', function (t) {
t.deepEqual(detective(src), [ 'a', 'b', 'c', 'events', 'doom', 'y', 'events2' ]);
t.end();
});
+12
View File
@@ -0,0 +1,12 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/word.js');
test('word', function (t) {
t.deepEqual(
detective(src, { word : 'load' }),
[ 'a', 'b', 'c', 'events', 'doom', 'y', 'events2' ]
);
t.end();
});
+9
View File
@@ -0,0 +1,9 @@
var test = require('tap').test;
var detective = require('../');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/files/yield.js');
test('yield', function (t) {
t.plan(1);
t.deepEqual(detective(src), [ 'a', 'c' ]);
});
+15
View File
@@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+377
View File
@@ -0,0 +1,377 @@
[![Build Status](https://travis-ci.org/isaacs/node-glob.svg?branch=master)](https://travis-ci.org/isaacs/node-glob/) [![Dependency Status](https://david-dm.org/isaacs/node-glob.svg)](https://david-dm.org/isaacs/node-glob) [![devDependency Status](https://david-dm.org/isaacs/node-glob/dev-status.svg)](https://david-dm.org/isaacs/node-glob#info=devDependencies) [![optionalDependency Status](https://david-dm.org/isaacs/node-glob/optional-status.svg)](https://david-dm.org/isaacs/node-glob#info=optionalDependencies)
# Glob
Match files using the patterns the shell uses, like stars and stuff.
This is a glob implementation in JavaScript. It uses the `minimatch`
library to do its matching.
![](oh-my-glob.gif)
## Usage
```javascript
var glob = require("glob")
// options is optional
glob("**/*.js", options, function (er, files) {
// files is an array of filenames.
// If the `nonull` option is set, and nothing
// was found, then files is ["**/*.js"]
// er is an error object or null.
})
```
## Glob Primer
"Globs" are the patterns you type when you do stuff like `ls *.js` on
the command line, or put `build/*` in a `.gitignore` file.
Before parsing the path part patterns, braced sections are expanded
into a set. Braced sections start with `{` and end with `}`, with any
number of comma-delimited sections within. Braced sections may contain
slash characters, so `a{/b/c,bcd}` would expand into `a/b/c` and `abcd`.
The following characters have special magic meaning when used in a
path portion:
* `*` Matches 0 or more characters in a single path portion
* `?` Matches 1 character
* `[...]` Matches a range of characters, similar to a RegExp range.
If the first character of the range is `!` or `^` then it matches
any character not in the range.
* `!(pattern|pattern|pattern)` Matches anything that does not match
any of the patterns provided.
* `?(pattern|pattern|pattern)` Matches zero or one occurrence of the
patterns provided.
* `+(pattern|pattern|pattern)` Matches one or more occurrences of the
patterns provided.
* `*(a|b|c)` Matches zero or more occurrences of the patterns provided
* `@(pattern|pat*|pat?erN)` Matches exactly one of the patterns
provided
* `**` If a "globstar" is alone in a path portion, then it matches
zero or more directories and subdirectories searching for matches.
It does not crawl symlinked directories.
### Dots
If a file or directory path portion has a `.` as the first character,
then it will not match any glob pattern unless that pattern's
corresponding path part also has a `.` as its first character.
For example, the pattern `a/.*/c` would match the file at `a/.b/c`.
However the pattern `a/*/c` would not, because `*` does not start with
a dot character.
You can make glob treat dots as normal characters by setting
`dot:true` in the options.
### Basename Matching
If you set `matchBase:true` in the options, and the pattern has no
slashes in it, then it will seek for any file anywhere in the tree
with a matching basename. For example, `*.js` would match
`test/simple/basic.js`.
### Negation
The intent for negation would be for a pattern starting with `!` to
match everything that *doesn't* match the supplied pattern. However,
the implementation is weird, and for the time being, this should be
avoided. The behavior is deprecated in version 5, and will be removed
entirely in version 6.
### Empty Sets
If no matching files are found, then an empty array is returned. This
differs from the shell, where the pattern itself is returned. For
example:
$ echo a*s*d*f
a*s*d*f
To get the bash-style behavior, set the `nonull:true` in the options.
### See Also:
* `man sh`
* `man bash` (Search for "Pattern Matching")
* `man 3 fnmatch`
* `man 5 gitignore`
* [minimatch documentation](https://github.com/isaacs/minimatch)
## glob.hasMagic(pattern, [options])
Returns `true` if there are any special characters in the pattern, and
`false` otherwise.
Note that the options affect the results. If `noext:true` is set in
the options object, then `+(a|b)` will not be considered a magic
pattern. If the pattern has a brace expansion, like `a/{b/c,x/y}`
then that is considered magical, unless `nobrace:true` is set in the
options.
## glob(pattern, [options], cb)
* `pattern` {String} Pattern to be matched
* `options` {Object}
* `cb` {Function}
* `err` {Error | null}
* `matches` {Array<String>} filenames found matching the pattern
Perform an asynchronous glob search.
## glob.sync(pattern, [options])
* `pattern` {String} Pattern to be matched
* `options` {Object}
* return: {Array<String>} filenames found matching the pattern
Perform a synchronous glob search.
## Class: glob.Glob
Create a Glob object by instantiating the `glob.Glob` class.
```javascript
var Glob = require("glob").Glob
var mg = new Glob(pattern, options, cb)
```
It's an EventEmitter, and starts walking the filesystem to find matches
immediately.
### new glob.Glob(pattern, [options], [cb])
* `pattern` {String} pattern to search for
* `options` {Object}
* `cb` {Function} Called when an error occurs, or matches are found
* `err` {Error | null}
* `matches` {Array<String>} filenames found matching the pattern
Note that if the `sync` flag is set in the options, then matches will
be immediately available on the `g.found` member.
### Properties
* `minimatch` The minimatch object that the glob uses.
* `options` The options object passed in.
* `aborted` Boolean which is set to true when calling `abort()`. There
is no way at this time to continue a glob search after aborting, but
you can re-use the statCache to avoid having to duplicate syscalls.
* `cache` Convenience object. Each field has the following possible
values:
* `false` - Path does not exist
* `true` - Path exists
* `'DIR'` - Path exists, and is not a directory
* `'FILE'` - Path exists, and is a directory
* `[file, entries, ...]` - Path exists, is a directory, and the
array value is the results of `fs.readdir`
* `statCache` Cache of `fs.stat` results, to prevent statting the same
path multiple times.
* `symlinks` A record of which paths are symbolic links, which is
relevant in resolving `**` patterns.
* `realpathCache` An optional object which is passed to `fs.realpath`
to minimize unnecessary syscalls. It is stored on the instantiated
Glob object, and may be re-used.
### Events
* `end` When the matching is finished, this is emitted with all the
matches found. If the `nonull` option is set, and no match was found,
then the `matches` list contains the original pattern. The matches
are sorted, unless the `nosort` flag is set.
* `match` Every time a match is found, this is emitted with the matched.
* `error` Emitted when an unexpected error is encountered, or whenever
any fs error occurs if `options.strict` is set.
* `abort` When `abort()` is called, this event is raised.
### Methods
* `pause` Temporarily stop the search
* `resume` Resume the search
* `abort` Stop the search forever
### Options
All the options that can be passed to Minimatch can also be passed to
Glob to change pattern matching behavior. Also, some have been added,
or have glob-specific ramifications.
All options are false by default, unless otherwise noted.
All options are added to the Glob object, as well.
If you are running many `glob` operations, you can pass a Glob object
as the `options` argument to a subsequent operation to shortcut some
`stat` and `readdir` calls. At the very least, you may pass in shared
`symlinks`, `statCache`, `realpathCache`, and `cache` options, so that
parallel glob operations will be sped up by sharing information about
the filesystem.
* `cwd` The current working directory in which to search. Defaults
to `process.cwd()`.
* `root` The place where patterns starting with `/` will be mounted
onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix
systems, and `C:\` or some such on Windows.)
* `dot` Include `.dot` files in normal matches and `globstar` matches.
Note that an explicit dot in a portion of the pattern will always
match dot files.
* `nomount` By default, a pattern starting with a forward-slash will be
"mounted" onto the root setting, so that a valid filesystem path is
returned. Set this flag to disable that behavior.
* `mark` Add a `/` character to directory matches. Note that this
requires additional stat calls.
* `nosort` Don't sort the results.
* `stat` Set to true to stat *all* results. This reduces performance
somewhat, and is completely unnecessary, unless `readdir` is presumed
to be an untrustworthy indicator of file existence.
* `silent` When an unusual error is encountered when attempting to
read a directory, a warning will be printed to stderr. Set the
`silent` option to true to suppress these warnings.
* `strict` When an unusual error is encountered when attempting to
read a directory, the process will just continue on in search of
other matches. Set the `strict` option to raise an error in these
cases.
* `cache` See `cache` property above. Pass in a previously generated
cache object to save some fs calls.
* `statCache` A cache of results of filesystem information, to prevent
unnecessary stat calls. While it should not normally be necessary
to set this, you may pass the statCache from one glob() call to the
options object of another, if you know that the filesystem will not
change between calls. (See "Race Conditions" below.)
* `symlinks` A cache of known symbolic links. You may pass in a
previously generated `symlinks` object to save `lstat` calls when
resolving `**` matches.
* `sync` DEPRECATED: use `glob.sync(pattern, opts)` instead.
* `nounique` In some cases, brace-expanded patterns can result in the
same file showing up multiple times in the result set. By default,
this implementation prevents duplicates in the result set. Set this
flag to disable that behavior.
* `nonull` Set to never return an empty set, instead returning a set
containing the pattern itself. This is the default in glob(3).
* `debug` Set to enable debug logging in minimatch and glob.
* `nobrace` Do not expand `{a,b}` and `{1..3}` brace sets.
* `noglobstar` Do not match `**` against multiple filenames. (Ie,
treat it as a normal `*` instead.)
* `noext` Do not match `+(a|b)` "extglob" patterns.
* `nocase` Perform a case-insensitive match. Note: on
case-insensitive filesystems, non-magic patterns will match by
default, since `stat` and `readdir` will not raise errors.
* `matchBase` Perform a basename-only match if the pattern does not
contain any slash characters. That is, `*.js` would be treated as
equivalent to `**/*.js`, matching all js files in all directories.
* `nodir` Do not match directories, only files. (Note: to match
*only* directories, simply put a `/` at the end of the pattern.)
* `ignore` Add a pattern or an array of patterns to exclude matches.
* `follow` Follow symlinked directories when expanding `**` patterns.
Note that this can result in a lot of duplicate references in the
presence of cyclic links.
* `realpath` Set to true to call `fs.realpath` on all of the results.
In the case of a symlink that cannot be resolved, the full absolute
path to the matched entry is returned (though it will usually be a
broken symlink)
* `nonegate` Suppress deprecated `negate` behavior. (See below.)
Default=true
* `nocomment` Suppress deprecated `comment` behavior. (See below.)
Default=true
## Comparisons to other fnmatch/glob implementations
While strict compliance with the existing standards is a worthwhile
goal, some discrepancies exist between node-glob and other
implementations, and are intentional.
The double-star character `**` is supported by default, unless the
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.3, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.
Note that symlinked directories are not crawled as part of a `**`,
though their contents may match against subsequent portions of the
pattern. This prevents infinite loops and duplicates and the like.
If an escaped pattern has no matches, and the `nonull` flag is set,
then glob returns the pattern as-provided, rather than
interpreting the character escapes. For example,
`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.
If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern. Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity. Since those two are valid, matching proceeds.
### Comments and Negation
**Note**: In version 5 of this module, negation and comments are
**disabled** by default. You can explicitly set `nonegate:false` or
`nocomment:false` to re-enable them. They are going away entirely in
version 6.
The intent for negation would be for a pattern starting with `!` to
match everything that *doesn't* match the supplied pattern. However,
the implementation is weird. It is better to use the `ignore` option
to set a pattern or set of patterns to exclude from matches. If you
want the "everything except *x*" type of behavior, you can use `**` as
the main pattern, and set an `ignore` for the things to exclude.
The comments feature is added in minimatch, primarily to more easily
support use cases like ignore files, where a `#` at the start of a
line makes the pattern "empty". However, in the context of a
straightforward filesystem globber, "comments" don't make much sense.
## Windows
**Please only use forward-slashes in glob expressions.**
Though windows uses either `/` or `\` as its path separator, only `/`
characters are used by this glob implementation. You must use
forward-slashes **only** in glob expressions. Back-slashes will always
be interpreted as escape characters, not path separators.
Results from absolute patterns such as `/foo/*` are mounted onto the
root setting using `path.join`. On windows, this will by default result
in `/foo/*` matching `C:\foo\bar.txt`.
## Race Conditions
Glob searching, by its very nature, is susceptible to race conditions,
since it relies on directory walking and such.
As a result, it is possible that a file that exists when glob looks for
it may have been deleted or modified by the time it returns the result.
As part of its internal implementation, this program caches all stat
and readdir calls that it makes, in order to cut down on system
overhead. However, this also makes it even more susceptible to races,
especially if the cache or statCache objects are reused between glob
calls.
Users are thus advised not to use a glob result as a guarantee of
filesystem state in the face of rapid changes. For the vast majority
of operations, this is never a problem.
## Contributing
Any change to behavior (including bugfixes) must come with a test.
Patches that fail tests or reduce performance will be rejected.
```
# to run tests
npm test
# to re-generate test fixtures
npm run test-regen
# to benchmark against bash/zsh
npm run bench
# to profile javascript
npm run prof
```
+245
View File
@@ -0,0 +1,245 @@
exports.alphasort = alphasort
exports.alphasorti = alphasorti
exports.setopts = setopts
exports.ownProp = ownProp
exports.makeAbs = makeAbs
exports.finish = finish
exports.mark = mark
exports.isIgnored = isIgnored
exports.childrenIgnored = childrenIgnored
function ownProp (obj, field) {
return Object.prototype.hasOwnProperty.call(obj, field)
}
var path = require("path")
var minimatch = require("minimatch")
var isAbsolute = require("path-is-absolute")
var Minimatch = minimatch.Minimatch
function alphasorti (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase())
}
function alphasort (a, b) {
return a.localeCompare(b)
}
function setupIgnores (self, options) {
self.ignore = options.ignore || []
if (!Array.isArray(self.ignore))
self.ignore = [self.ignore]
if (self.ignore.length) {
self.ignore = self.ignore.map(ignoreMap)
}
}
function ignoreMap (pattern) {
var gmatcher = null
if (pattern.slice(-3) === '/**') {
var gpattern = pattern.replace(/(\/\*\*)+$/, '')
gmatcher = new Minimatch(gpattern)
}
return {
matcher: new Minimatch(pattern),
gmatcher: gmatcher
}
}
function setopts (self, pattern, options) {
if (!options)
options = {}
// base-matching: just use globstar for that.
if (options.matchBase && -1 === pattern.indexOf("/")) {
if (options.noglobstar) {
throw new Error("base matching requires globstar")
}
pattern = "**/" + pattern
}
self.silent = !!options.silent
self.pattern = pattern
self.strict = options.strict !== false
self.realpath = !!options.realpath
self.realpathCache = options.realpathCache || Object.create(null)
self.follow = !!options.follow
self.dot = !!options.dot
self.mark = !!options.mark
self.nodir = !!options.nodir
if (self.nodir)
self.mark = true
self.sync = !!options.sync
self.nounique = !!options.nounique
self.nonull = !!options.nonull
self.nosort = !!options.nosort
self.nocase = !!options.nocase
self.stat = !!options.stat
self.noprocess = !!options.noprocess
self.maxLength = options.maxLength || Infinity
self.cache = options.cache || Object.create(null)
self.statCache = options.statCache || Object.create(null)
self.symlinks = options.symlinks || Object.create(null)
setupIgnores(self, options)
self.changedCwd = false
var cwd = process.cwd()
if (!ownProp(options, "cwd"))
self.cwd = cwd
else {
self.cwd = options.cwd
self.changedCwd = path.resolve(options.cwd) !== cwd
}
self.root = options.root || path.resolve(self.cwd, "/")
self.root = path.resolve(self.root)
if (process.platform === "win32")
self.root = self.root.replace(/\\/g, "/")
self.nomount = !!options.nomount
// disable comments and negation unless the user explicitly
// passes in false as the option.
options.nonegate = options.nonegate === false ? false : true
options.nocomment = options.nocomment === false ? false : true
deprecationWarning(options)
self.minimatch = new Minimatch(pattern, options)
self.options = self.minimatch.options
}
// TODO(isaacs): remove entirely in v6
// exported to reset in tests
exports.deprecationWarned
function deprecationWarning(options) {
if (!options.nonegate || !options.nocomment) {
if (process.noDeprecation !== true && !exports.deprecationWarned) {
var msg = 'glob WARNING: comments and negation will be disabled in v6'
if (process.throwDeprecation)
throw new Error(msg)
else if (process.traceDeprecation)
console.trace(msg)
else
console.error(msg)
exports.deprecationWarned = true
}
}
}
function finish (self) {
var nou = self.nounique
var all = nou ? [] : Object.create(null)
for (var i = 0, l = self.matches.length; i < l; i ++) {
var matches = self.matches[i]
if (!matches || Object.keys(matches).length === 0) {
if (self.nonull) {
// do like the shell, and spit out the literal glob
var literal = self.minimatch.globSet[i]
if (nou)
all.push(literal)
else
all[literal] = true
}
} else {
// had matches
var m = Object.keys(matches)
if (nou)
all.push.apply(all, m)
else
m.forEach(function (m) {
all[m] = true
})
}
}
if (!nou)
all = Object.keys(all)
if (!self.nosort)
all = all.sort(self.nocase ? alphasorti : alphasort)
// at *some* point we statted all of these
if (self.mark) {
for (var i = 0; i < all.length; i++) {
all[i] = self._mark(all[i])
}
if (self.nodir) {
all = all.filter(function (e) {
return !(/\/$/.test(e))
})
}
}
if (self.ignore.length)
all = all.filter(function(m) {
return !isIgnored(self, m)
})
self.found = all
}
function mark (self, p) {
var abs = makeAbs(self, p)
var c = self.cache[abs]
var m = p
if (c) {
var isDir = c === 'DIR' || Array.isArray(c)
var slash = p.slice(-1) === '/'
if (isDir && !slash)
m += '/'
else if (!isDir && slash)
m = m.slice(0, -1)
if (m !== p) {
var mabs = makeAbs(self, m)
self.statCache[mabs] = self.statCache[abs]
self.cache[mabs] = self.cache[abs]
}
}
return m
}
// lotta situps...
function makeAbs (self, f) {
var abs = f
if (f.charAt(0) === '/') {
abs = path.join(self.root, f)
} else if (isAbsolute(f) || f === '') {
abs = f
} else if (self.changedCwd) {
abs = path.resolve(self.cwd, f)
} else {
abs = path.resolve(f)
}
return abs
}
// Return true, if pattern ends with globstar '**', for the accompanying parent directory.
// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents
function isIgnored (self, path) {
if (!self.ignore.length)
return false
return self.ignore.some(function(item) {
return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path))
})
}
function childrenIgnored (self, path) {
if (!self.ignore.length)
return false
return self.ignore.some(function(item) {
return !!(item.gmatcher && item.gmatcher.match(path))
})
}
+752
View File
@@ -0,0 +1,752 @@
// Approach:
//
// 1. Get the minimatch set
// 2. For each pattern in the set, PROCESS(pattern, false)
// 3. Store matches per-set, then uniq them
//
// PROCESS(pattern, inGlobStar)
// Get the first [n] items from pattern that are all strings
// Join these together. This is PREFIX.
// If there is no more remaining, then stat(PREFIX) and
// add to matches if it succeeds. END.
//
// If inGlobStar and PREFIX is symlink and points to dir
// set ENTRIES = []
// else readdir(PREFIX) as ENTRIES
// If fail, END
//
// with ENTRIES
// If pattern[n] is GLOBSTAR
// // handle the case where the globstar match is empty
// // by pruning it out, and testing the resulting pattern
// PROCESS(pattern[0..n] + pattern[n+1 .. $], false)
// // handle other cases.
// for ENTRY in ENTRIES (not dotfiles)
// // attach globstar + tail onto the entry
// // Mark that this entry is a globstar match
// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true)
//
// else // not globstar
// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
// Test ENTRY against pattern[n]
// If fails, continue
// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
//
// Caveat:
// Cache all stats and readdirs results to minimize syscall. Since all
// we ever care about is existence and directory-ness, we can just keep
// `true` for files, and [children,...] for directories, or `false` for
// things that don't exist.
module.exports = glob
var fs = require('fs')
var minimatch = require('minimatch')
var Minimatch = minimatch.Minimatch
var inherits = require('inherits')
var EE = require('events').EventEmitter
var path = require('path')
var assert = require('assert')
var isAbsolute = require('path-is-absolute')
var globSync = require('./sync.js')
var common = require('./common.js')
var alphasort = common.alphasort
var alphasorti = common.alphasorti
var setopts = common.setopts
var ownProp = common.ownProp
var inflight = require('inflight')
var util = require('util')
var childrenIgnored = common.childrenIgnored
var isIgnored = common.isIgnored
var once = require('once')
function glob (pattern, options, cb) {
if (typeof options === 'function') cb = options, options = {}
if (!options) options = {}
if (options.sync) {
if (cb)
throw new TypeError('callback provided to sync glob')
return globSync(pattern, options)
}
return new Glob(pattern, options, cb)
}
glob.sync = globSync
var GlobSync = glob.GlobSync = globSync.GlobSync
// old api surface
glob.glob = glob
glob.hasMagic = function (pattern, options_) {
var options = util._extend({}, options_)
options.noprocess = true
var g = new Glob(pattern, options)
var set = g.minimatch.set
if (set.length > 1)
return true
for (var j = 0; j < set[0].length; j++) {
if (typeof set[0][j] !== 'string')
return true
}
return false
}
glob.Glob = Glob
inherits(Glob, EE)
function Glob (pattern, options, cb) {
if (typeof options === 'function') {
cb = options
options = null
}
if (options && options.sync) {
if (cb)
throw new TypeError('callback provided to sync glob')
return new GlobSync(pattern, options)
}
if (!(this instanceof Glob))
return new Glob(pattern, options, cb)
setopts(this, pattern, options)
this._didRealPath = false
// process each pattern in the minimatch set
var n = this.minimatch.set.length
// The matches are stored as {<filename>: true,...} so that
// duplicates are automagically pruned.
// Later, we do an Object.keys() on these.
// Keep them as a list so we can fill in when nonull is set.
this.matches = new Array(n)
if (typeof cb === 'function') {
cb = once(cb)
this.on('error', cb)
this.on('end', function (matches) {
cb(null, matches)
})
}
var self = this
var n = this.minimatch.set.length
this._processing = 0
this.matches = new Array(n)
this._emitQueue = []
this._processQueue = []
this.paused = false
if (this.noprocess)
return this
if (n === 0)
return done()
for (var i = 0; i < n; i ++) {
this._process(this.minimatch.set[i], i, false, done)
}
function done () {
--self._processing
if (self._processing <= 0)
self._finish()
}
}
Glob.prototype._finish = function () {
assert(this instanceof Glob)
if (this.aborted)
return
if (this.realpath && !this._didRealpath)
return this._realpath()
common.finish(this)
this.emit('end', this.found)
}
Glob.prototype._realpath = function () {
if (this._didRealpath)
return
this._didRealpath = true
var n = this.matches.length
if (n === 0)
return this._finish()
var self = this
for (var i = 0; i < this.matches.length; i++)
this._realpathSet(i, next)
function next () {
if (--n === 0)
self._finish()
}
}
Glob.prototype._realpathSet = function (index, cb) {
var matchset = this.matches[index]
if (!matchset)
return cb()
var found = Object.keys(matchset)
var self = this
var n = found.length
if (n === 0)
return cb()
var set = this.matches[index] = Object.create(null)
found.forEach(function (p, i) {
// If there's a problem with the stat, then it means that
// one or more of the links in the realpath couldn't be
// resolved. just return the abs value in that case.
p = self._makeAbs(p)
fs.realpath(p, self.realpathCache, function (er, real) {
if (!er)
set[real] = true
else if (er.syscall === 'stat')
set[p] = true
else
self.emit('error', er) // srsly wtf right here
if (--n === 0) {
self.matches[index] = set
cb()
}
})
})
}
Glob.prototype._mark = function (p) {
return common.mark(this, p)
}
Glob.prototype._makeAbs = function (f) {
return common.makeAbs(this, f)
}
Glob.prototype.abort = function () {
this.aborted = true
this.emit('abort')
}
Glob.prototype.pause = function () {
if (!this.paused) {
this.paused = true
this.emit('pause')
}
}
Glob.prototype.resume = function () {
if (this.paused) {
this.emit('resume')
this.paused = false
if (this._emitQueue.length) {
var eq = this._emitQueue.slice(0)
this._emitQueue.length = 0
for (var i = 0; i < eq.length; i ++) {
var e = eq[i]
this._emitMatch(e[0], e[1])
}
}
if (this._processQueue.length) {
var pq = this._processQueue.slice(0)
this._processQueue.length = 0
for (var i = 0; i < pq.length; i ++) {
var p = pq[i]
this._processing--
this._process(p[0], p[1], p[2], p[3])
}
}
}
}
Glob.prototype._process = function (pattern, index, inGlobStar, cb) {
assert(this instanceof Glob)
assert(typeof cb === 'function')
if (this.aborted)
return
this._processing++
if (this.paused) {
this._processQueue.push([pattern, index, inGlobStar, cb])
return
}
//console.error('PROCESS %d', this._processing, pattern)
// Get the first [n] parts of pattern that are all strings.
var n = 0
while (typeof pattern[n] === 'string') {
n ++
}
// now n is the index of the first one that is *not* a string.
// see if there's anything else
var prefix
switch (n) {
// if not, then this is rather simple
case pattern.length:
this._processSimple(pattern.join('/'), index, cb)
return
case 0:
// pattern *starts* with some non-trivial item.
// going to readdir(cwd), but not include the prefix in matches.
prefix = null
break
default:
// pattern has some string bits in the front.
// whatever it starts with, whether that's 'absolute' like /foo/bar,
// or 'relative' like '../baz'
prefix = pattern.slice(0, n).join('/')
break
}
var remain = pattern.slice(n)
// get the list of entries.
var read
if (prefix === null)
read = '.'
else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
if (!prefix || !isAbsolute(prefix))
prefix = '/' + prefix
read = prefix
} else
read = prefix
var abs = this._makeAbs(read)
//if ignored, skip _processing
if (childrenIgnored(this, read))
return cb()
var isGlobStar = remain[0] === minimatch.GLOBSTAR
if (isGlobStar)
this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)
else
this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)
}
Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {
var self = this
this._readdir(abs, inGlobStar, function (er, entries) {
return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
})
}
Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
// if the abs isn't a dir, then nothing can match!
if (!entries)
return cb()
// It will only match dot entries if it starts with a dot, or if
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
var pn = remain[0]
var negate = !!this.minimatch.negate
var rawGlob = pn._glob
var dotOk = this.dot || rawGlob.charAt(0) === '.'
var matchedEntries = []
for (var i = 0; i < entries.length; i++) {
var e = entries[i]
if (e.charAt(0) !== '.' || dotOk) {
var m
if (negate && !prefix) {
m = !e.match(pn)
} else {
m = e.match(pn)
}
if (m)
matchedEntries.push(e)
}
}
//console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)
var len = matchedEntries.length
// If there are no matched entries, then nothing matches.
if (len === 0)
return cb()
// if this is the last remaining pattern bit, then no need for
// an additional stat *unless* the user has specified mark or
// stat explicitly. We know they exist, since readdir returned
// them.
if (remain.length === 1 && !this.mark && !this.stat) {
if (!this.matches[index])
this.matches[index] = Object.create(null)
for (var i = 0; i < len; i ++) {
var e = matchedEntries[i]
if (prefix) {
if (prefix !== '/')
e = prefix + '/' + e
else
e = prefix + e
}
if (e.charAt(0) === '/' && !this.nomount) {
e = path.join(this.root, e)
}
this._emitMatch(index, e)
}
// This was the last one, and no stats were needed
return cb()
}
// now test all matched entries as stand-ins for that part
// of the pattern.
remain.shift()
for (var i = 0; i < len; i ++) {
var e = matchedEntries[i]
var newPattern
if (prefix) {
if (prefix !== '/')
e = prefix + '/' + e
else
e = prefix + e
}
this._process([e].concat(remain), index, inGlobStar, cb)
}
cb()
}
Glob.prototype._emitMatch = function (index, e) {
if (this.aborted)
return
if (this.matches[index][e])
return
if (isIgnored(this, e))
return
if (this.paused) {
this._emitQueue.push([index, e])
return
}
var abs = this._makeAbs(e)
if (this.nodir) {
var c = this.cache[abs]
if (c === 'DIR' || Array.isArray(c))
return
}
if (this.mark)
e = this._mark(e)
this.matches[index][e] = true
var st = this.statCache[abs]
if (st)
this.emit('stat', e, st)
this.emit('match', e)
}
Glob.prototype._readdirInGlobStar = function (abs, cb) {
if (this.aborted)
return
// follow all symlinked directories forever
// just proceed as if this is a non-globstar situation
if (this.follow)
return this._readdir(abs, false, cb)
var lstatkey = 'lstat\0' + abs
var self = this
var lstatcb = inflight(lstatkey, lstatcb_)
if (lstatcb)
fs.lstat(abs, lstatcb)
function lstatcb_ (er, lstat) {
if (er)
return cb()
var isSym = lstat.isSymbolicLink()
self.symlinks[abs] = isSym
// If it's not a symlink or a dir, then it's definitely a regular file.
// don't bother doing a readdir in that case.
if (!isSym && !lstat.isDirectory()) {
self.cache[abs] = 'FILE'
cb()
} else
self._readdir(abs, false, cb)
}
}
Glob.prototype._readdir = function (abs, inGlobStar, cb) {
if (this.aborted)
return
cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb)
if (!cb)
return
//console.error('RD %j %j', +inGlobStar, abs)
if (inGlobStar && !ownProp(this.symlinks, abs))
return this._readdirInGlobStar(abs, cb)
if (ownProp(this.cache, abs)) {
var c = this.cache[abs]
if (!c || c === 'FILE')
return cb()
if (Array.isArray(c))
return cb(null, c)
}
var self = this
fs.readdir(abs, readdirCb(this, abs, cb))
}
function readdirCb (self, abs, cb) {
return function (er, entries) {
if (er)
self._readdirError(abs, er, cb)
else
self._readdirEntries(abs, entries, cb)
}
}
Glob.prototype._readdirEntries = function (abs, entries, cb) {
if (this.aborted)
return
// if we haven't asked to stat everything, then just
// assume that everything in there exists, so we can avoid
// having to stat it a second time.
if (!this.mark && !this.stat) {
for (var i = 0; i < entries.length; i ++) {
var e = entries[i]
if (abs === '/')
e = abs + e
else
e = abs + '/' + e
this.cache[e] = true
}
}
this.cache[abs] = entries
return cb(null, entries)
}
Glob.prototype._readdirError = function (f, er, cb) {
if (this.aborted)
return
// handle errors, and cache the information
switch (er.code) {
case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
case 'ENOTDIR': // totally normal. means it *does* exist.
this.cache[this._makeAbs(f)] = 'FILE'
break
case 'ENOENT': // not terribly unusual
case 'ELOOP':
case 'ENAMETOOLONG':
case 'UNKNOWN':
this.cache[this._makeAbs(f)] = false
break
default: // some unusual error. Treat as failure.
this.cache[this._makeAbs(f)] = false
if (this.strict) {
this.emit('error', er)
// If the error is handled, then we abort
// if not, we threw out of here
this.abort()
}
if (!this.silent)
console.error('glob error', er)
break
}
return cb()
}
Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {
var self = this
this._readdir(abs, inGlobStar, function (er, entries) {
self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
})
}
Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
//console.error('pgs2', prefix, remain[0], entries)
// no entries means not a dir, so it can never have matches
// foo.txt/** doesn't match foo.txt
if (!entries)
return cb()
// test without the globstar, and with every child both below
// and replacing the globstar.
var remainWithoutGlobStar = remain.slice(1)
var gspref = prefix ? [ prefix ] : []
var noGlobStar = gspref.concat(remainWithoutGlobStar)
// the noGlobStar pattern exits the inGlobStar state
this._process(noGlobStar, index, false, cb)
var isSym = this.symlinks[abs]
var len = entries.length
// If it's a symlink, and we're in a globstar, then stop
if (isSym && inGlobStar)
return cb()
for (var i = 0; i < len; i++) {
var e = entries[i]
if (e.charAt(0) === '.' && !this.dot)
continue
// these two cases enter the inGlobStar state
var instead = gspref.concat(entries[i], remainWithoutGlobStar)
this._process(instead, index, true, cb)
var below = gspref.concat(entries[i], remain)
this._process(below, index, true, cb)
}
cb()
}
Glob.prototype._processSimple = function (prefix, index, cb) {
// XXX review this. Shouldn't it be doing the mounting etc
// before doing stat? kinda weird?
var self = this
this._stat(prefix, function (er, exists) {
self._processSimple2(prefix, index, er, exists, cb)
})
}
Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {
//console.error('ps2', prefix, exists)
if (!this.matches[index])
this.matches[index] = Object.create(null)
// If it doesn't exist, then just mark the lack of results
if (!exists)
return cb()
if (prefix && isAbsolute(prefix) && !this.nomount) {
var trail = /[\/\\]$/.test(prefix)
if (prefix.charAt(0) === '/') {
prefix = path.join(this.root, prefix)
} else {
prefix = path.resolve(this.root, prefix)
if (trail)
prefix += '/'
}
}
if (process.platform === 'win32')
prefix = prefix.replace(/\\/g, '/')
// Mark this as a match
this._emitMatch(index, prefix)
cb()
}
// Returns either 'DIR', 'FILE', or false
Glob.prototype._stat = function (f, cb) {
var abs = this._makeAbs(f)
var needDir = f.slice(-1) === '/'
if (f.length > this.maxLength)
return cb()
if (!this.stat && ownProp(this.cache, abs)) {
var c = this.cache[abs]
if (Array.isArray(c))
c = 'DIR'
// It exists, but maybe not how we need it
if (!needDir || c === 'DIR')
return cb(null, c)
if (needDir && c === 'FILE')
return cb()
// otherwise we have to stat, because maybe c=true
// if we know it exists, but not what it is.
}
var exists
var stat = this.statCache[abs]
if (stat !== undefined) {
if (stat === false)
return cb(null, stat)
else {
var type = stat.isDirectory() ? 'DIR' : 'FILE'
if (needDir && type === 'FILE')
return cb()
else
return cb(null, type, stat)
}
}
var self = this
var statcb = inflight('stat\0' + abs, lstatcb_)
if (statcb)
fs.lstat(abs, statcb)
function lstatcb_ (er, lstat) {
if (lstat && lstat.isSymbolicLink()) {
// If it's a symlink, then treat it as the target, unless
// the target does not exist, then treat it as a file.
return fs.stat(abs, function (er, stat) {
if (er)
self._stat2(f, abs, null, lstat, cb)
else
self._stat2(f, abs, er, stat, cb)
})
} else {
self._stat2(f, abs, er, lstat, cb)
}
}
}
Glob.prototype._stat2 = function (f, abs, er, stat, cb) {
if (er) {
this.statCache[abs] = false
return cb()
}
var needDir = f.slice(-1) === '/'
this.statCache[abs] = stat
if (abs.slice(-1) === '/' && !stat.isDirectory())
return cb(null, false, stat)
var c = stat.isDirectory() ? 'DIR' : 'FILE'
this.cache[abs] = this.cache[abs] || c
if (needDir && c !== 'DIR')
return cb()
return cb(null, c, stat)
}
+75
View File
@@ -0,0 +1,75 @@
{
"_from": "glob@^5.0.15",
"_id": "glob@5.0.15",
"_inBundle": false,
"_integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
"_location": "/commoner/glob",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "glob@^5.0.15",
"name": "glob",
"escapedName": "glob",
"rawSpec": "^5.0.15",
"saveSpec": null,
"fetchSpec": "^5.0.15"
},
"_requiredBy": [
"/commoner"
],
"_resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
"_shasum": "1bc936b9e02f4a603fcc222ecf7633d30b8b93b1",
"_spec": "glob@^5.0.15",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/commoner",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"bugs": {
"url": "https://github.com/isaacs/node-glob/issues"
},
"bundleDependencies": false,
"dependencies": {
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "2 || 3",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"deprecated": false,
"description": "a little globber",
"devDependencies": {
"mkdirp": "0",
"rimraf": "^2.2.8",
"tap": "^1.1.4",
"tick": "0.0.6"
},
"engines": {
"node": "*"
},
"files": [
"glob.js",
"sync.js",
"common.js"
],
"homepage": "https://github.com/isaacs/node-glob#readme",
"license": "ISC",
"main": "glob.js",
"name": "glob",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-glob.git"
},
"scripts": {
"bench": "bash benchmark.sh",
"benchclean": "node benchclean.js",
"prepublish": "npm run benchclean",
"prof": "bash prof.sh && cat profile.txt",
"profclean": "rm -f v8.log profile.txt",
"test": "tap test/*.js --cov",
"test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js"
},
"version": "5.0.15"
}
+460
View File
@@ -0,0 +1,460 @@
module.exports = globSync
globSync.GlobSync = GlobSync
var fs = require('fs')
var minimatch = require('minimatch')
var Minimatch = minimatch.Minimatch
var Glob = require('./glob.js').Glob
var util = require('util')
var path = require('path')
var assert = require('assert')
var isAbsolute = require('path-is-absolute')
var common = require('./common.js')
var alphasort = common.alphasort
var alphasorti = common.alphasorti
var setopts = common.setopts
var ownProp = common.ownProp
var childrenIgnored = common.childrenIgnored
function globSync (pattern, options) {
if (typeof options === 'function' || arguments.length === 3)
throw new TypeError('callback provided to sync glob\n'+
'See: https://github.com/isaacs/node-glob/issues/167')
return new GlobSync(pattern, options).found
}
function GlobSync (pattern, options) {
if (!pattern)
throw new Error('must provide pattern')
if (typeof options === 'function' || arguments.length === 3)
throw new TypeError('callback provided to sync glob\n'+
'See: https://github.com/isaacs/node-glob/issues/167')
if (!(this instanceof GlobSync))
return new GlobSync(pattern, options)
setopts(this, pattern, options)
if (this.noprocess)
return this
var n = this.minimatch.set.length
this.matches = new Array(n)
for (var i = 0; i < n; i ++) {
this._process(this.minimatch.set[i], i, false)
}
this._finish()
}
GlobSync.prototype._finish = function () {
assert(this instanceof GlobSync)
if (this.realpath) {
var self = this
this.matches.forEach(function (matchset, index) {
var set = self.matches[index] = Object.create(null)
for (var p in matchset) {
try {
p = self._makeAbs(p)
var real = fs.realpathSync(p, self.realpathCache)
set[real] = true
} catch (er) {
if (er.syscall === 'stat')
set[self._makeAbs(p)] = true
else
throw er
}
}
})
}
common.finish(this)
}
GlobSync.prototype._process = function (pattern, index, inGlobStar) {
assert(this instanceof GlobSync)
// Get the first [n] parts of pattern that are all strings.
var n = 0
while (typeof pattern[n] === 'string') {
n ++
}
// now n is the index of the first one that is *not* a string.
// See if there's anything else
var prefix
switch (n) {
// if not, then this is rather simple
case pattern.length:
this._processSimple(pattern.join('/'), index)
return
case 0:
// pattern *starts* with some non-trivial item.
// going to readdir(cwd), but not include the prefix in matches.
prefix = null
break
default:
// pattern has some string bits in the front.
// whatever it starts with, whether that's 'absolute' like /foo/bar,
// or 'relative' like '../baz'
prefix = pattern.slice(0, n).join('/')
break
}
var remain = pattern.slice(n)
// get the list of entries.
var read
if (prefix === null)
read = '.'
else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
if (!prefix || !isAbsolute(prefix))
prefix = '/' + prefix
read = prefix
} else
read = prefix
var abs = this._makeAbs(read)
//if ignored, skip processing
if (childrenIgnored(this, read))
return
var isGlobStar = remain[0] === minimatch.GLOBSTAR
if (isGlobStar)
this._processGlobStar(prefix, read, abs, remain, index, inGlobStar)
else
this._processReaddir(prefix, read, abs, remain, index, inGlobStar)
}
GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) {
var entries = this._readdir(abs, inGlobStar)
// if the abs isn't a dir, then nothing can match!
if (!entries)
return
// It will only match dot entries if it starts with a dot, or if
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
var pn = remain[0]
var negate = !!this.minimatch.negate
var rawGlob = pn._glob
var dotOk = this.dot || rawGlob.charAt(0) === '.'
var matchedEntries = []
for (var i = 0; i < entries.length; i++) {
var e = entries[i]
if (e.charAt(0) !== '.' || dotOk) {
var m
if (negate && !prefix) {
m = !e.match(pn)
} else {
m = e.match(pn)
}
if (m)
matchedEntries.push(e)
}
}
var len = matchedEntries.length
// If there are no matched entries, then nothing matches.
if (len === 0)
return
// if this is the last remaining pattern bit, then no need for
// an additional stat *unless* the user has specified mark or
// stat explicitly. We know they exist, since readdir returned
// them.
if (remain.length === 1 && !this.mark && !this.stat) {
if (!this.matches[index])
this.matches[index] = Object.create(null)
for (var i = 0; i < len; i ++) {
var e = matchedEntries[i]
if (prefix) {
if (prefix.slice(-1) !== '/')
e = prefix + '/' + e
else
e = prefix + e
}
if (e.charAt(0) === '/' && !this.nomount) {
e = path.join(this.root, e)
}
this.matches[index][e] = true
}
// This was the last one, and no stats were needed
return
}
// now test all matched entries as stand-ins for that part
// of the pattern.
remain.shift()
for (var i = 0; i < len; i ++) {
var e = matchedEntries[i]
var newPattern
if (prefix)
newPattern = [prefix, e]
else
newPattern = [e]
this._process(newPattern.concat(remain), index, inGlobStar)
}
}
GlobSync.prototype._emitMatch = function (index, e) {
var abs = this._makeAbs(e)
if (this.mark)
e = this._mark(e)
if (this.matches[index][e])
return
if (this.nodir) {
var c = this.cache[this._makeAbs(e)]
if (c === 'DIR' || Array.isArray(c))
return
}
this.matches[index][e] = true
if (this.stat)
this._stat(e)
}
GlobSync.prototype._readdirInGlobStar = function (abs) {
// follow all symlinked directories forever
// just proceed as if this is a non-globstar situation
if (this.follow)
return this._readdir(abs, false)
var entries
var lstat
var stat
try {
lstat = fs.lstatSync(abs)
} catch (er) {
// lstat failed, doesn't exist
return null
}
var isSym = lstat.isSymbolicLink()
this.symlinks[abs] = isSym
// If it's not a symlink or a dir, then it's definitely a regular file.
// don't bother doing a readdir in that case.
if (!isSym && !lstat.isDirectory())
this.cache[abs] = 'FILE'
else
entries = this._readdir(abs, false)
return entries
}
GlobSync.prototype._readdir = function (abs, inGlobStar) {
var entries
if (inGlobStar && !ownProp(this.symlinks, abs))
return this._readdirInGlobStar(abs)
if (ownProp(this.cache, abs)) {
var c = this.cache[abs]
if (!c || c === 'FILE')
return null
if (Array.isArray(c))
return c
}
try {
return this._readdirEntries(abs, fs.readdirSync(abs))
} catch (er) {
this._readdirError(abs, er)
return null
}
}
GlobSync.prototype._readdirEntries = function (abs, entries) {
// if we haven't asked to stat everything, then just
// assume that everything in there exists, so we can avoid
// having to stat it a second time.
if (!this.mark && !this.stat) {
for (var i = 0; i < entries.length; i ++) {
var e = entries[i]
if (abs === '/')
e = abs + e
else
e = abs + '/' + e
this.cache[e] = true
}
}
this.cache[abs] = entries
// mark and cache dir-ness
return entries
}
GlobSync.prototype._readdirError = function (f, er) {
// handle errors, and cache the information
switch (er.code) {
case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
case 'ENOTDIR': // totally normal. means it *does* exist.
this.cache[this._makeAbs(f)] = 'FILE'
break
case 'ENOENT': // not terribly unusual
case 'ELOOP':
case 'ENAMETOOLONG':
case 'UNKNOWN':
this.cache[this._makeAbs(f)] = false
break
default: // some unusual error. Treat as failure.
this.cache[this._makeAbs(f)] = false
if (this.strict)
throw er
if (!this.silent)
console.error('glob error', er)
break
}
}
GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
var entries = this._readdir(abs, inGlobStar)
// no entries means not a dir, so it can never have matches
// foo.txt/** doesn't match foo.txt
if (!entries)
return
// test without the globstar, and with every child both below
// and replacing the globstar.
var remainWithoutGlobStar = remain.slice(1)
var gspref = prefix ? [ prefix ] : []
var noGlobStar = gspref.concat(remainWithoutGlobStar)
// the noGlobStar pattern exits the inGlobStar state
this._process(noGlobStar, index, false)
var len = entries.length
var isSym = this.symlinks[abs]
// If it's a symlink, and we're in a globstar, then stop
if (isSym && inGlobStar)
return
for (var i = 0; i < len; i++) {
var e = entries[i]
if (e.charAt(0) === '.' && !this.dot)
continue
// these two cases enter the inGlobStar state
var instead = gspref.concat(entries[i], remainWithoutGlobStar)
this._process(instead, index, true)
var below = gspref.concat(entries[i], remain)
this._process(below, index, true)
}
}
GlobSync.prototype._processSimple = function (prefix, index) {
// XXX review this. Shouldn't it be doing the mounting etc
// before doing stat? kinda weird?
var exists = this._stat(prefix)
if (!this.matches[index])
this.matches[index] = Object.create(null)
// If it doesn't exist, then just mark the lack of results
if (!exists)
return
if (prefix && isAbsolute(prefix) && !this.nomount) {
var trail = /[\/\\]$/.test(prefix)
if (prefix.charAt(0) === '/') {
prefix = path.join(this.root, prefix)
} else {
prefix = path.resolve(this.root, prefix)
if (trail)
prefix += '/'
}
}
if (process.platform === 'win32')
prefix = prefix.replace(/\\/g, '/')
// Mark this as a match
this.matches[index][prefix] = true
}
// Returns either 'DIR', 'FILE', or false
GlobSync.prototype._stat = function (f) {
var abs = this._makeAbs(f)
var needDir = f.slice(-1) === '/'
if (f.length > this.maxLength)
return false
if (!this.stat && ownProp(this.cache, abs)) {
var c = this.cache[abs]
if (Array.isArray(c))
c = 'DIR'
// It exists, but maybe not how we need it
if (!needDir || c === 'DIR')
return c
if (needDir && c === 'FILE')
return false
// otherwise we have to stat, because maybe c=true
// if we know it exists, but not what it is.
}
var exists
var stat = this.statCache[abs]
if (!stat) {
var lstat
try {
lstat = fs.lstatSync(abs)
} catch (er) {
return false
}
if (lstat.isSymbolicLink()) {
try {
stat = fs.statSync(abs)
} catch (er) {
stat = lstat
}
} else {
stat = lstat
}
}
this.statCache[abs] = stat
var c = stat.isDirectory() ? 'DIR' : 'FILE'
this.cache[abs] = this.cache[abs] || c
if (needDir && c !== 'DIR')
return false
return c
}
GlobSync.prototype._mark = function (p) {
return common.mark(this, p)
}
GlobSync.prototype._makeAbs = function (f) {
return common.makeAbs(this, f)
}
+88
View File
@@ -0,0 +1,88 @@
{
"_from": "commoner@~0.10.0",
"_id": "commoner@0.10.8",
"_inBundle": false,
"_integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=",
"_location": "/commoner",
"_phantomChildren": {
"defined": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
},
"_requested": {
"type": "range",
"registry": true,
"raw": "commoner@~0.10.0",
"name": "commoner",
"escapedName": "commoner",
"rawSpec": "~0.10.0",
"saveSpec": null,
"fetchSpec": "~0.10.0"
},
"_requiredBy": [
"/regenerator-6to5"
],
"_resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz",
"_shasum": "34fc3672cd24393e8bb47e70caa0293811f4f2c5",
"_spec": "commoner@~0.10.0",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/regenerator-6to5",
"author": {
"name": "Ben Newman",
"email": "ben@benjamn.com"
},
"bin": {
"commonize": "./bin/commonize"
},
"bugs": {
"url": "https://github.com/benjamn/commoner/issues"
},
"bundleDependencies": false,
"dependencies": {
"commander": "^2.5.0",
"detective": "^4.3.1",
"glob": "^5.0.15",
"graceful-fs": "^4.1.2",
"iconv-lite": "^0.4.5",
"mkdirp": "^0.5.0",
"private": "^0.1.6",
"q": "^1.1.2",
"recast": "^0.11.17"
},
"deprecated": false,
"description": "Flexible tool for translating any dialect of JavaScript into Node-readable CommonJS modules",
"devDependencies": {
"mocha": "^2.3.3"
},
"engines": {
"node": ">= 0.8"
},
"files": [
"bin",
"lib",
"main.js"
],
"homepage": "http://github.com/benjamn/commoner",
"keywords": [
"modules",
"require",
"commonjs",
"exports",
"commoner",
"browserify",
"stitch"
],
"license": "MIT",
"main": "main.js",
"name": "commoner",
"repository": {
"type": "git",
"url": "git://github.com/benjamn/commoner.git"
},
"scripts": {
"test": "rm -rf test/output ; node ./node_modules/mocha/bin/mocha --reporter spec test/run.js"
},
"version": "0.10.8"
}