fix(codemod): only count real module specifiers in project-type inference (#2765)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5119ee7fd7
commit
5ecc791d81
1 changed files with 122 additions and 18 deletions
|
|
@ -180,6 +180,74 @@ describe('analyzeProject', () => {
|
|||
expect(analyzeProject(dir).projectType).toBe('client');
|
||||
});
|
||||
|
||||
it('ignores an SDK subpath that appears only in a string literal (not a module specifier)', () => {
|
||||
// A real client import plus a server subpath stored as data in an ordinary string
|
||||
// literal. Counting quoted paths anywhere would flip this to "both" (rewriting shared
|
||||
// imports to the server package and adding a server dependency to a client-only
|
||||
// project); only genuine module specifiers may contribute to inference (#2760).
|
||||
const dir = v1Project({
|
||||
'a.ts': [
|
||||
`import { Client } from '@modelcontextprotocol/sdk/client/index.js';`,
|
||||
`const example = '@modelcontextprotocol/sdk/server/mcp.js';`,
|
||||
''
|
||||
].join('\n')
|
||||
});
|
||||
expect(analyzeProject(dir).projectType).toBe('client');
|
||||
});
|
||||
|
||||
it('still infers from dynamic import() and require() specifiers', () => {
|
||||
const dir = v1Project({
|
||||
'a.ts': `const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');`,
|
||||
'b.cjs': `const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');`
|
||||
});
|
||||
expect(analyzeProject(dir).projectType).toBe('both');
|
||||
});
|
||||
|
||||
it('still infers from a side-effect import and an export-from re-export', () => {
|
||||
const dir = v1Project({
|
||||
'a.ts': `import '@modelcontextprotocol/sdk/client/index.js';`,
|
||||
'b.ts': `export { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';`
|
||||
});
|
||||
expect(analyzeProject(dir).projectType).toBe('both');
|
||||
});
|
||||
|
||||
it('still infers from vi./jest. mock-method specifiers (the forms the mock-paths transform rewrites)', () => {
|
||||
const dir = v1Project({
|
||||
'a.test.ts': `vi.mock('@modelcontextprotocol/sdk/client/index.js');`,
|
||||
'b.test.ts': `const actual = jest.requireActual('@modelcontextprotocol/sdk/server/mcp.js');`
|
||||
});
|
||||
expect(analyzeProject(dir).projectType).toBe('both');
|
||||
});
|
||||
|
||||
it('still infers from a dynamic import() carrying a webpack magic comment', () => {
|
||||
const dir = v1Project({
|
||||
'a.ts': `const mod = await import(/* webpackChunkName: "mcp-client" */ '@modelcontextprotocol/sdk/client/index.js');`
|
||||
});
|
||||
expect(analyzeProject(dir).projectType).toBe('client');
|
||||
});
|
||||
|
||||
it('still infers from require.resolve()', () => {
|
||||
const dir = v1Project({
|
||||
'a.cjs': `const p = require.resolve('@modelcontextprotocol/sdk/server/mcp.js');`
|
||||
});
|
||||
expect(analyzeProject(dir).projectType).toBe('server');
|
||||
});
|
||||
|
||||
it('documents a known limitation: a string whose text embeds a full import statement still counts', () => {
|
||||
// The scan is lexical: the inner `from '` puts the quoted path in a specifier position
|
||||
// even though it sits inside string data. Distinguishing that from a real import needs
|
||||
// parsing, which the budget-bounded scan deliberately avoids. If the analyzer ever gets
|
||||
// smart enough to make this fail, flip the expectation to 'client'.
|
||||
const dir = v1Project({
|
||||
'a.ts': [
|
||||
`import { Client } from '@modelcontextprotocol/sdk/client/index.js';`,
|
||||
`const help = "import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'";`,
|
||||
''
|
||||
].join('\n')
|
||||
});
|
||||
expect(analyzeProject(dir).projectType).toBe('both');
|
||||
});
|
||||
|
||||
it('infers from source even without a package.json', () => {
|
||||
const dir = createTempDir();
|
||||
mkdirSync(path.join(dir, 'src'), { recursive: true });
|
||||
|
|
|
|||
Loading…
Reference in a new issue