2020-08-01 03:43:15 +02:00
|
|
|
"use strict";
|
|
|
|
|
2021-03-10 05:24:58 +01:00
|
|
|
const Module = require("module");
|
2020-10-07 10:48:54 +02:00
|
|
|
const path = require("path");
|
|
|
|
|
2021-03-10 06:10:32 +01:00
|
|
|
const callsites = require("callsites");
|
|
|
|
|
2019-07-25 09:13:22 +02:00
|
|
|
const new_globals = new Set();
|
|
|
|
let old_globals = {};
|
2016-07-30 17:00:12 +02:00
|
|
|
|
2021-03-10 05:24:58 +01:00
|
|
|
let actual_load;
|
2021-03-10 05:30:09 +01:00
|
|
|
const module_mocks = new Map();
|
2021-03-10 05:45:29 +01:00
|
|
|
const used_module_mocks = new Set();
|
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
|
|
|
|
2021-03-11 05:43:45 +01:00
|
|
|
const jquery_path = require.resolve("jquery");
|
|
|
|
const real_jquery_path = require.resolve("../zjsunit/real_jquery.js");
|
|
|
|
|
2021-03-10 05:24:58 +01:00
|
|
|
function load(request, parent, isMain) {
|
2021-03-10 05:55:04 +01:00
|
|
|
const filename = Module._resolveFilename(request, parent, isMain);
|
|
|
|
if (module_mocks.has(filename)) {
|
|
|
|
used_module_mocks.add(filename);
|
|
|
|
return module_mocks.get(filename);
|
2021-03-10 05:24:58 +01:00
|
|
|
}
|
2021-03-11 05:43:45 +01:00
|
|
|
if (filename === jquery_path && parent.filename !== real_jquery_path) {
|
|
|
|
// jQuery exposes an incompatible API to Node vs. browser, so
|
|
|
|
// this wouldn't work.
|
|
|
|
throw new Error("This test will need jquery mocked using zjquery or real_jquery");
|
|
|
|
}
|
2021-03-10 05:24:58 +01:00
|
|
|
|
|
|
|
return actual_load(request, parent, isMain);
|
|
|
|
}
|
|
|
|
|
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
|
|
|
exports.start = () => {
|
2021-03-10 05:24:58 +01:00
|
|
|
if (actual_load !== undefined) {
|
|
|
|
throw new Error("namespace.start was called twice in a row.");
|
|
|
|
}
|
|
|
|
actual_load = Module._load;
|
|
|
|
Module._load = load;
|
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
|
|
|
};
|
|
|
|
|
2021-03-11 05:13:50 +01:00
|
|
|
// We provide `mock_cjs` for mocking a CommonJS module, and `mock_esm` for
|
|
|
|
// mocking an ES6 module.
|
|
|
|
//
|
|
|
|
// A CommonJS module:
|
|
|
|
// - loads other modules using `require()`,
|
|
|
|
// - assigns its public contents to the `exports` object or `module.exports`,
|
|
|
|
// - consists of a single JavaScript value, typically an object or function,
|
|
|
|
// - when imported by an ES6 module:
|
|
|
|
// * is shallow-copied to a collection of immutable bindings, if it's an
|
|
|
|
// object,
|
|
|
|
// * is converted to a single default binding, if not.
|
|
|
|
//
|
|
|
|
// An ES6 module:
|
|
|
|
// - loads other modules using `import`,
|
|
|
|
// - declares its public contents using `export` statements,
|
|
|
|
// - consists of a collection of live bindings that may be mutated from inside
|
|
|
|
// but not outside the module,
|
|
|
|
// - may have a default binding (that's just syntactic sugar for a binding
|
|
|
|
// named `default`),
|
|
|
|
// - when required by a CommonJS module, always appears as an object.
|
|
|
|
//
|
|
|
|
// Most of our own modules are ES6 modules.
|
|
|
|
//
|
|
|
|
// For a third party module available in both formats that might present two
|
|
|
|
// incompatible APIs (especially if the CommonJS module is a function),
|
|
|
|
// Webpack will prefer the ES6 module if its availability is indicated by the
|
|
|
|
// "module" field of package.json, while Node.js will not; we need to mock the
|
|
|
|
// format preferred by Webpack.
|
|
|
|
|
2021-06-11 14:51:07 +02:00
|
|
|
function resolve_filename(fn) {
|
2021-03-10 06:10:32 +01:00
|
|
|
const filename = Module._resolveFilename(
|
2021-06-11 14:51:07 +02:00
|
|
|
fn,
|
2021-03-10 06:10:32 +01:00
|
|
|
require.cache[callsites()[1].getFileName()],
|
|
|
|
false,
|
|
|
|
);
|
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
|
|
|
|
2021-03-10 05:55:04 +01:00
|
|
|
if (module_mocks.has(filename)) {
|
|
|
|
throw new Error(`You already set up a mock for ${filename}`);
|
2021-03-10 05:38:28 +01:00
|
|
|
}
|
|
|
|
|
2021-03-10 06:03:25 +01:00
|
|
|
if (filename in require.cache) {
|
|
|
|
throw new Error(`It is too late to mock ${filename}; call this earlier.`);
|
|
|
|
}
|
|
|
|
|
2021-06-11 14:51:07 +02:00
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.mock_cjs = (fn, obj) => {
|
|
|
|
const filename = resolve_filename(fn);
|
2021-03-10 05:55:04 +01:00
|
|
|
module_mocks.set(filename, obj);
|
2021-03-06 19:05:17 +01:00
|
|
|
return obj;
|
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
|
|
|
};
|
|
|
|
|
2021-06-11 14:51:07 +02:00
|
|
|
exports.mock_template = (fn) => {
|
|
|
|
const filename = resolve_filename("../../static/templates/" + fn);
|
|
|
|
|
|
|
|
const obj = {
|
|
|
|
f: () => {
|
|
|
|
throw new Error(`
|
|
|
|
You did render_foo = mock_template("${fn}")
|
|
|
|
but you also need to do override(render_foo, "f", () => {...}
|
|
|
|
`);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
module_mocks.set(filename, (...args) => obj.f(...args));
|
|
|
|
return obj;
|
|
|
|
};
|
|
|
|
|
2021-03-12 08:55:38 +01:00
|
|
|
exports.mock_esm = (request, obj = {}) => {
|
|
|
|
if (typeof obj !== "object") {
|
|
|
|
throw new TypeError("An ES module must be mocked with an object");
|
|
|
|
}
|
|
|
|
return exports.mock_cjs(request, {...obj, __esModule: true});
|
|
|
|
};
|
|
|
|
|
2021-03-25 21:38:40 +01:00
|
|
|
exports.unmock_module = (request) => {
|
|
|
|
const filename = Module._resolveFilename(
|
|
|
|
request,
|
|
|
|
require.cache[callsites()[1].getFileName()],
|
|
|
|
false,
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!module_mocks.has(filename)) {
|
|
|
|
throw new Error(`Cannot unmock ${filename}, which was not mocked`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!used_module_mocks.has(filename)) {
|
|
|
|
throw new Error(`You asked to mock ${filename} but we never saw it during compilation.`);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_mocks.delete(filename);
|
|
|
|
used_module_mocks.delete(filename);
|
|
|
|
};
|
|
|
|
|
2016-07-30 17:00:12 +02:00
|
|
|
exports.set_global = function (name, val) {
|
2021-02-22 15:34:23 +01:00
|
|
|
if (val === null) {
|
|
|
|
throw new Error(`
|
|
|
|
We try to avoid using null in our codebase.
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
|
2019-07-25 09:13:22 +02:00
|
|
|
if (!(name in old_globals)) {
|
|
|
|
if (!(name in global)) {
|
|
|
|
new_globals.add(name);
|
|
|
|
}
|
|
|
|
old_globals[name] = global[name];
|
|
|
|
}
|
2016-07-30 17:00:12 +02:00
|
|
|
global[name] = val;
|
|
|
|
return val;
|
|
|
|
};
|
|
|
|
|
2021-03-10 05:55:04 +01:00
|
|
|
exports.zrequire = function (short_fn) {
|
|
|
|
return require(`../../static/js/${short_fn}`);
|
node_tests: Don't remove require cache of module in zrequire.
There is good reason to do this (explanation is bit long!). With the
TypeScript migration, and the require and ES6 migrations that come
with it, we use require instead of set_global which loads the entire
module. Suppose we have a util module, which is used by some other
module, say message_store, and util is being required in message_store
since it is removed from window. Then, if a test zrequires
message_store first, and then zrequires the util module qand mocks one
of its methods, it will not be mocked for the message_store
module. The reason is:
1. zrequire('message_store') leads to require('util').
2. zrequire('util') removes the util module from cache and it is
reloaded. Now the util module in message_store and the one in
the test will be different and any updates to it in tests won't
be reflected in the actual code.
Which can lead to confusion for folks writing tests. I'll mention this
can be avoided doing zrequire('util') first but...that is not ideal.
And, since there was one outlier test that relied on this behavior,
we add the namespace.reset_module function.
2020-08-19 17:35:27 +02:00
|
|
|
};
|
|
|
|
|
2021-02-22 19:51:28 +01:00
|
|
|
const staticPath = path.resolve(__dirname, "../../static") + path.sep;
|
|
|
|
const templatesPath = staticPath + "templates" + path.sep;
|
2020-02-27 15:40:59 +01:00
|
|
|
|
2021-03-13 14:14:40 +01:00
|
|
|
exports.complain_about_unused_mocks = function () {
|
|
|
|
for (const filename of module_mocks.keys()) {
|
|
|
|
if (!used_module_mocks.has(filename)) {
|
2021-03-16 23:38:59 +01:00
|
|
|
console.error(`You asked to mock ${filename} but we never saw it during compilation.`);
|
2021-03-13 14:14:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
|
|
|
exports.finish = function () {
|
|
|
|
/*
|
|
|
|
Handle cleanup tasks after we've run one module.
|
|
|
|
|
|
|
|
Note that we currently do lazy compilation of modules,
|
|
|
|
so we need to wait till the module tests finish
|
|
|
|
running to do things like detecting pointless mocks
|
|
|
|
and resetting our _load hook.
|
|
|
|
*/
|
2021-03-10 05:24:58 +01:00
|
|
|
if (actual_load === undefined) {
|
|
|
|
throw new Error("namespace.finish was called without namespace.start.");
|
|
|
|
}
|
|
|
|
Module._load = actual_load;
|
|
|
|
actual_load = undefined;
|
|
|
|
|
2021-03-10 06:04:09 +01:00
|
|
|
module_mocks.clear();
|
|
|
|
used_module_mocks.clear();
|
zjsunit: Remove rewiremock dependency.
We now just use a module._load hook to inject
stubs into our code.
For conversion purposes I temporarily maintain
the API of rewiremock, apart from the enable/disable
pieces, but I will make a better wrapper in an
upcoming commit.
We can detect when rewiremock is called after
zrequire now, and I fix all the violations in
this commit, mostly by using override.
We can also detect when a mock is needlessly
created, and I fix all the violations in this
commit.
The one minor nuisance that this commit introduces
is that you can only stub out modules in the Zulip
source tree, which is now static/js. This should
not really be a problem--there are usually better
techniques to deal with third party depenencies.
In the prior commit I show a typical workaround,
which is to create a one-line wrapper in your
test code. It's often the case that you can simply
use override(), as well.
In passing I kill off `reset_modules`, and I
eliminated the second argument to zrequire,
which dates back to pre-es6 days.
2021-03-06 12:47:54 +01:00
|
|
|
|
2021-02-22 19:51:28 +01:00
|
|
|
for (const path of Object.keys(require.cache)) {
|
|
|
|
if (path.startsWith(staticPath) && !path.startsWith(templatesPath)) {
|
|
|
|
delete require.cache[path];
|
|
|
|
}
|
2021-01-22 22:29:08 +01:00
|
|
|
}
|
2020-02-09 04:15:38 +01:00
|
|
|
Object.assign(global, old_globals);
|
2019-07-25 09:13:22 +02:00
|
|
|
old_globals = {};
|
|
|
|
for (const name of new_globals) {
|
|
|
|
delete global[name];
|
|
|
|
}
|
|
|
|
new_globals.clear();
|
2016-07-30 17:00:12 +02:00
|
|
|
};
|
|
|
|
|
2020-07-27 16:06:46 +02:00
|
|
|
exports.with_field = function (obj, field, val, f) {
|
2021-02-26 22:01:14 +01:00
|
|
|
if ("__esModule" in obj && "__Rewire__" in obj) {
|
|
|
|
const old_val = field in obj ? obj[field] : obj.__GetDependency__(field);
|
|
|
|
try {
|
|
|
|
obj.__Rewire__(field, val);
|
|
|
|
return f();
|
|
|
|
} finally {
|
|
|
|
obj.__Rewire__(field, old_val);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const had_val = Object.prototype.hasOwnProperty.call(obj, field);
|
|
|
|
const old_val = obj[field];
|
|
|
|
try {
|
|
|
|
obj[field] = val;
|
|
|
|
return f();
|
|
|
|
} finally {
|
|
|
|
if (had_val) {
|
|
|
|
obj[field] = old_val;
|
|
|
|
} else {
|
|
|
|
delete obj[field];
|
|
|
|
}
|
2021-02-23 04:54:34 +01:00
|
|
|
}
|
2021-02-23 04:54:23 +01:00
|
|
|
}
|
2020-07-27 16:06:46 +02:00
|
|
|
};
|
|
|
|
|
2017-03-11 21:07:24 +01:00
|
|
|
exports.with_overrides = function (test_function) {
|
|
|
|
// This function calls test_function() and passes in
|
|
|
|
// a way to override the namespace temporarily.
|
|
|
|
|
2020-07-26 13:21:15 +02:00
|
|
|
const restore_callbacks = [];
|
2020-07-26 13:32:02 +02:00
|
|
|
const unused_funcs = new Map();
|
2021-02-12 15:47:26 +01:00
|
|
|
const override = function (obj, func_name, f) {
|
|
|
|
// Given an object `obj` (which is usually a module object),
|
|
|
|
// we re-map `obj[func_name]` to the `f` passed in by the caller.
|
|
|
|
// Then the outer function here (`with_overrides`) automatically
|
|
|
|
// restores the original value of `obj[func_name]` as its last
|
|
|
|
// step. Generally our code calls `run_test`, which wraps
|
|
|
|
// `with_overrides`.
|
2020-07-26 14:31:16 +02:00
|
|
|
if (typeof f !== "function") {
|
2021-02-13 13:20:29 +01:00
|
|
|
throw new TypeError(
|
|
|
|
"You can only override with a function. Use with_field for non-functions.",
|
|
|
|
);
|
2020-07-26 14:31:16 +02:00
|
|
|
}
|
2021-02-22 15:34:23 +01:00
|
|
|
|
|
|
|
if (typeof obj !== "object" && typeof obj !== "function") {
|
|
|
|
throw new TypeError(`We cannot override a function for ${typeof obj} objects`);
|
|
|
|
}
|
|
|
|
|
2021-02-24 23:01:21 +01:00
|
|
|
if (obj[func_name] !== undefined && typeof obj[func_name] !== "function") {
|
2021-02-22 15:34:23 +01:00
|
|
|
throw new TypeError(`
|
|
|
|
You are overriding a non-function with a function.
|
|
|
|
This is almost certainly an error.
|
|
|
|
`);
|
|
|
|
}
|
2020-07-26 14:31:16 +02:00
|
|
|
|
2021-02-12 15:47:26 +01:00
|
|
|
if (!unused_funcs.has(obj)) {
|
|
|
|
unused_funcs.set(obj, new Map());
|
2017-03-12 14:32:12 +01:00
|
|
|
}
|
2021-02-11 14:19:12 +01:00
|
|
|
|
2021-02-12 15:47:26 +01:00
|
|
|
unused_funcs.get(obj).set(func_name, true);
|
2017-03-12 14:32:12 +01:00
|
|
|
|
2021-03-11 18:01:52 +01:00
|
|
|
const old_f =
|
2021-02-20 05:15:54 +01:00
|
|
|
"__esModule" in obj && "__Rewire__" in obj && !(func_name in obj)
|
|
|
|
? obj.__GetDependency__(func_name)
|
|
|
|
: obj[func_name];
|
2021-02-22 12:18:06 +01:00
|
|
|
|
|
|
|
const new_f = function (...args) {
|
2021-02-12 15:47:26 +01:00
|
|
|
unused_funcs.get(obj).delete(func_name);
|
2020-07-26 13:32:02 +02:00
|
|
|
return f.apply(this, args);
|
|
|
|
};
|
2017-03-11 21:07:24 +01:00
|
|
|
|
2021-02-22 12:18:06 +01:00
|
|
|
// Let zjquery know this function was patched with override,
|
|
|
|
// so it doesn't complain about us modifying it. (Other
|
|
|
|
// code can also use this, as needed.)
|
|
|
|
new_f._patched_with_override = true;
|
|
|
|
|
2021-02-20 05:15:54 +01:00
|
|
|
if ("__esModule" in obj && "__Rewire__" in obj) {
|
|
|
|
obj.__Rewire__(func_name, new_f);
|
|
|
|
restore_callbacks.push(() => {
|
|
|
|
obj.__Rewire__(func_name, old_f);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
obj[func_name] = new_f;
|
|
|
|
restore_callbacks.push(() => {
|
|
|
|
obj[func_name] = old_f;
|
|
|
|
});
|
|
|
|
}
|
2017-03-11 21:07:24 +01:00
|
|
|
};
|
|
|
|
|
2021-02-23 04:54:23 +01:00
|
|
|
try {
|
|
|
|
test_function(override);
|
|
|
|
} finally {
|
|
|
|
restore_callbacks.reverse();
|
|
|
|
for (const restore_callback of restore_callbacks) {
|
|
|
|
restore_callback();
|
|
|
|
}
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2020-07-26 13:32:02 +02:00
|
|
|
|
2021-02-11 01:23:23 +01:00
|
|
|
for (const module_unused_funcs of unused_funcs.values()) {
|
|
|
|
for (const unused_name of module_unused_funcs.keys()) {
|
|
|
|
throw new Error(unused_name + " never got invoked!");
|
|
|
|
}
|
2020-07-26 13:32:02 +02:00
|
|
|
}
|
2017-03-11 21:07:24 +01:00
|
|
|
};
|