mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-17 05:07:37 +08:00
e77ca83621
Four gaps left CommonJS calls on the local alias variable - or on the wrong
function:
- `require('../')` up to the project root produced an empty relative path, so
its `/index.js` candidate was never found;
- a default import never followed `module.exports = X` /
`exports = module.exports = X` / `module.exports = require('./lib/x')`;
- only the first declarator of `var a = require('x'), b = require('y')` was a
binding, so `b()` - a package call - stayed on the local variable;
- `require('./utils').member` was read as the module's DEFAULT export: every
such alias in express resolved to utils' first exported function
(`normalizeType`). It is now the named member, and `exports.name =
function impl(...)`, `module.exports.name = impl` and `module.exports = { a,
b: c }` bind names the export index could not see.
express -1087 +727 (707 `express()` calls reach createApplication; supertest /
node:assert calls no longer edge to the local variable), eslint -750 +827
(`new RuleTester()` reaches the class, not another test's variable), mocha
-83 +90 (`run(...)` no longer lands in another spec file), koa 65 wrong
`request` edges -> `response`, body-parser +20. zod, trpc, hono, typeorm,
axios unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
/**
|
|
* Calls through CommonJS `require` bindings (express's shape):
|
|
* - `var express = require('../')` from `test/` is the project root's
|
|
* `index.js`, whose `module.exports = require('./lib/express')` forwards to
|
|
* `exports = module.exports = createApplication` — `express()` is that call;
|
|
* - each declarator of a list is a binding: `var express = require('../'),
|
|
* request = require('supertest')` — `request(app)` is the package's, no edge;
|
|
* - `require('./utils').setCharset` is the module's `setCharset`, written
|
|
* `exports.setCharset = function setCharset(…)`, not the module's first
|
|
* export (every such alias in express went to `normalizeType`).
|
|
*/
|
|
import { describe, it, expect, afterAll, beforeAll } from 'vitest';
|
|
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'path';
|
|
import { CodeGraph } from '../src';
|
|
|
|
let root = '';
|
|
let cg: CodeGraph;
|
|
|
|
beforeAll(async () => {
|
|
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-cjs-require-'));
|
|
const files: Record<string, string> = {
|
|
'package.json': JSON.stringify({ name: 'web', main: 'index.js', devDependencies: { supertest: '^6' } }),
|
|
'index.js': `'use strict';
|
|
|
|
module.exports = require('./lib/express');
|
|
`,
|
|
'lib/express.js': `'use strict';
|
|
|
|
exports = module.exports = createApplication;
|
|
|
|
function createApplication() {
|
|
return {};
|
|
}
|
|
`,
|
|
'lib/utils.js': `'use strict';
|
|
|
|
exports.normalizeType = function (type) {
|
|
return type;
|
|
};
|
|
|
|
exports.setCharset = function setCharset(type, charset) {
|
|
return type + charset;
|
|
};
|
|
`,
|
|
'lib/response.js': `'use strict';
|
|
|
|
var normalizeType = require('./utils').normalizeType;
|
|
var setCharset = require('./utils').setCharset;
|
|
|
|
function send(body) {
|
|
return setCharset(normalizeType(body), 'utf-8');
|
|
}
|
|
|
|
module.exports = { send };
|
|
`,
|
|
'test/app.js': `'use strict';
|
|
|
|
var express = require('../')
|
|
, request = require('supertest');
|
|
|
|
describe('app', function () {
|
|
it('responds', function () {
|
|
var app = express();
|
|
request(app);
|
|
});
|
|
});
|
|
`,
|
|
};
|
|
for (const [rel, content] of Object.entries(files)) {
|
|
fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true });
|
|
fs.writeFileSync(path.join(root, rel), content);
|
|
}
|
|
cg = await CodeGraph.init(root, { index: true });
|
|
});
|
|
|
|
afterAll(() => {
|
|
cg?.close();
|
|
if (root) fs.rmSync(root, { recursive: true, force: true });
|
|
});
|
|
|
|
/** `file: qualified name` of every call edge out of a file. */
|
|
function callsFrom(file: string): string[] {
|
|
const ids = cg.getNodesInFile(file).map((n) => n.id);
|
|
return cg
|
|
.getOutgoingEdgesFrom(ids)
|
|
.filter((e) => e.kind === 'calls')
|
|
.map((e) => {
|
|
const t = cg.getNode(e.target)!;
|
|
return `${t.filePath}: ${t.qualifiedName}`;
|
|
})
|
|
.sort();
|
|
}
|
|
|
|
describe('calls through CommonJS require bindings', () => {
|
|
it('follow `module.exports` through the root index to the exported function; an outside package is no edge', () => {
|
|
expect(callsFrom('test/app.js')).toEqual(['lib/express.js: createApplication']);
|
|
});
|
|
|
|
it('read a `require(…).member` alias as that member', () => {
|
|
expect(callsFrom('lib/response.js')).toEqual(['lib/utils.js: normalizeType', 'lib/utils.js: setCharset']);
|
|
});
|
|
});
|