mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-17 05:07:37 +08:00
fix(express): a function-expression handler is the route's body, like an arrow (#2261)
An Express handler written inline as `function (req, res) {…}` — the form
Express's own examples use — fell to the named-handler branch, so the route
linked to nothing and Steps drew a lone box for `POST /login`. The handler
is now the registration's LAST argument, inline as an arrow or a function
expression, bare or inside a wrapper call, past a trailing comma; an inline
middleware before a named handler no longer stands in for it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* An Express handler written inline as `function (req, res) {…}` — the form
|
||||
* Express's own examples use — is the route's body just like an arrow is:
|
||||
* its calls are the route's, so Steps draws what `POST /login` does instead
|
||||
* of a lone box. The handler is the LAST argument; an inline middleware
|
||||
* before a named handler is not it, and a trailing comma changes nothing.
|
||||
*/
|
||||
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-express-fnexpr-'));
|
||||
const files: Record<string, string> = {
|
||||
'package.json': JSON.stringify({ name: 'app', private: true, dependencies: { express: '^4.0.0' } }),
|
||||
'app.js': `const express = require('express');
|
||||
const app = express();
|
||||
const router = express.Router();
|
||||
|
||||
function authenticate(name, pass, fn) {
|
||||
fn(null, { name });
|
||||
}
|
||||
|
||||
function restrict(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
function loadUser(id) {
|
||||
return { id };
|
||||
}
|
||||
|
||||
function listPosts() {
|
||||
return [];
|
||||
}
|
||||
|
||||
function wrap(fn) {
|
||||
return fn;
|
||||
}
|
||||
|
||||
function showPost(req, res) {
|
||||
res.send('post');
|
||||
}
|
||||
|
||||
app.post('/login', function (req, res, next) {
|
||||
authenticate(req.body.username, req.body.password, function (err, user) {
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, function named(req, res) {
|
||||
res.send(loadUser(req.params.id));
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/posts',
|
||||
wrap(async function (req, res) {
|
||||
res.json(listPosts());
|
||||
}),
|
||||
);
|
||||
|
||||
app.get('/post/:id', (req, res, next) => next(), showPost);
|
||||
|
||||
router.route('/users/:id').get(function (req, res) {
|
||||
res.json(loadUser(req.params.id));
|
||||
});
|
||||
`,
|
||||
};
|
||||
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 });
|
||||
});
|
||||
|
||||
const fromRoute = (name: string, kind: string) => {
|
||||
const route = cg.getNodesByKind('route').find((r) => r.name === name);
|
||||
expect(route, name).toBeDefined();
|
||||
return cg.getOutgoingEdgesFrom([route!.id]).filter((e) => e.kind === kind).map((e) => cg.getNode(e.target)!.name);
|
||||
};
|
||||
|
||||
describe('Express function-expression handlers', () => {
|
||||
it('lend the route their calls', () => {
|
||||
expect(fromRoute('POST /login', 'calls')).toContain('authenticate');
|
||||
expect(fromRoute('GET /restricted', 'calls')).toContain('loadUser');
|
||||
expect(fromRoute('GET /users/:id', 'calls')).toContain('loadUser');
|
||||
});
|
||||
|
||||
it('are found inside a wrapper call, past a trailing comma', () => {
|
||||
expect(fromRoute('GET /posts', 'calls')).toContain('listPosts');
|
||||
});
|
||||
|
||||
it('leave a named last argument the handler, even after an inline middleware', () => {
|
||||
expect(fromRoute('GET /post/:id', 'references')).toContain('showPost');
|
||||
expect(fromRoute('GET /post/:id', 'calls')).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user