/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/npm/test/lib/commands/query.js
512 строк
14 KB
npm CLI robot
deps: upgrade npm to 11.18.0
08 июл 2026, 21:19
Не верифицирован
08 июл 2026, 21:19
fd35018
Код
Авторство
О чём код?
const t = require('tap') const { load: loadMockNpm } = require('../../fixtures/mock-npm') const { cleanCwd } = require('../../fixtures/clean-snapshot.js') t.cleanSnapshot = (str) => cleanCwd(str) t.test('simple query', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { prefixDir: { node_modules: { a: { name: 'a', version: '1.0.0', }, b: { name: 'b', version: '^2.0.0', }, }, 'package.json': JSON.stringify({ name: 'project', dependencies: { a: '^1.0.0', b: '^1.0.0', }, peerDependencies: { c: '1.0.0', }, }), }, }) await npm.exec('query', [':root, :root > *']) t.matchSnapshot(joinedOutput(), 'should return root object and direct children') }) t.test('recursive tree', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { prefixDir: { node_modules: { a: { name: 'a', version: '1.0.0', }, b: { name: 'b', version: '^2.0.0', dependencies: { a: '1.0.0', }, }, }, 'package.json': JSON.stringify({ name: 'project', dependencies: { a: '^1.0.0', b: '^1.0.0', }, }), }, }) await npm.exec('query', ['*']) t.matchSnapshot(joinedOutput(), 'should return everything in the tree, accounting for recursion') }) t.test('workspace query', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { config: { workspace: ['c'], }, prefixDir: { node_modules: { a: { name: 'a', version: '1.0.0', }, b: { name: 'b', version: '^2.0.0', }, c: t.fixture('symlink', '../c'), }, c: { 'package.json': JSON.stringify({ name: 'c', version: '1.0.0', }), }, 'package.json': JSON.stringify({ name: 'project', workspaces: ['c'], dependencies: { a: '^1.0.0', b: '^1.0.0', }, }), }, }) await npm.exec('query', [':scope']) t.matchSnapshot(joinedOutput(), 'should return workspace object') }) t.test('include-workspace-root', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { config: { 'include-workspace-root': true, workspace: ['c'], }, prefixDir: { node_modules: { a: { name: 'a', version: '1.0.0', }, b: { name: 'b', version: '^2.0.0', }, c: t.fixture('symlink', '../c'), }, c: { 'package.json': JSON.stringify({ name: 'c', version: '1.0.0', }), }, 'package.json': JSON.stringify({ name: 'project', workspaces: ['c'], dependencies: { a: '^1.0.0', b: '^1.0.0', }, }), }, }) await npm.exec('query', [':scope']) t.matchSnapshot(joinedOutput(), 'should return workspace object and root object') }) t.test('linked node', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { prefixDir: { node_modules: { a: t.fixture('symlink', '../a'), }, a: { 'package.json': JSON.stringify({ name: 'a', version: '1.0.0', }), }, 'package.json': JSON.stringify({ name: 'project', dependencies: { a: 'file:./a', }, }), }, }) await npm.exec('query', ['[name=a]']) t.matchSnapshot(joinedOutput(), 'should return linked node res') }) t.test('linked strategy reports logical location, not store backing path', async t => { /* linked layout: nopt (direct) symlinked to its store key, abbrev a transitive dep of nopt */ const linkedDir = { prefixDir: { node_modules: { nopt: t.fixture('symlink', '.store/nopt-hash/node_modules/nopt'), '.store': { 'nopt-hash': { node_modules: { nopt: { 'package.json': JSON.stringify({ name: 'nopt', version: '7.2.1', dependencies: { abbrev: '^2.0.0' }, }), }, abbrev: t.fixture('symlink', '../../abbrev-hash/node_modules/abbrev'), }, }, 'abbrev-hash': { node_modules: { abbrev: { 'package.json': JSON.stringify({ name: 'abbrev', version: '2.0.0' }), }, }, }, }, }, 'package.json': JSON.stringify({ name: 'project', version: '1.0.0', dependencies: { nopt: '^7.0.0' }, }), }, } await t.test(':root > * reports the logical link location', async t => { const { npm, joinedOutput } = await loadMockNpm(t, linkedDir) await npm.exec('query', [':root > *']) const res = JSON.parse(joinedOutput()) t.equal(res.length, 1, 'returns a single result, not the store node duplicate') t.equal(res[0].location, 'node_modules/nopt', 'reports the logical location') t.match(res[0].realpath, /\.store/, 'realpath still resolves to the store') }) await t.test(':root * keeps direct deps logical and transitive deps canonical', async t => { const { npm, joinedOutput } = await loadMockNpm(t, linkedDir) await npm.exec('query', [':root *']) const byName = Object.fromEntries(JSON.parse(joinedOutput()).map(n => [n.name, n.location])) t.equal(byName.nopt, 'node_modules/nopt', 'direct dep keeps its logical location') t.equal(byName.abbrev, 'node_modules/.store/abbrev-hash/node_modules/abbrev', 'transitive dep reports its canonical store key, not a consumer symlink') }) }) t.test('global', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { config: { global: true, }, globalPrefixDir: { node_modules: { lorem: { 'package.json': JSON.stringify({ name: 'lorem', version: '2.0.0', }), }, }, }, }) await npm.exec('query', ['[name=lorem]']) t.matchSnapshot(joinedOutput(), 'should return global package') }) t.test('package-lock-only', t => { t.test('no package lock', async t => { const { npm } = await loadMockNpm(t, { config: { 'package-lock-only': true, }, prefixDir: { 'package.json': JSON.stringify({ name: 'project', dependencies: { a: '^1.0.0', }, }), }, }) await t.rejects(npm.exec('query', [':root, :root > *']), { code: 'EUSAGE' }) }) t.test('with package lock', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { config: { 'package-lock-only': true, }, prefixDir: { 'package.json': JSON.stringify({ name: 'project', dependencies: { a: '^1.0.0', }, }), 'package-lock.json': JSON.stringify({ name: 'project', lockfileVersion: 3, requires: true, packages: { '': { dependencies: { a: '^1.0.0', }, }, 'node_modules/a': { version: '1.2.3', resolved: 'https://dummy.npmjs.org/a/-/a-1.2.3.tgz', integrity: 'sha512-dummy', engines: { node: '>=14.17', }, }, }, }), }, }) await npm.exec('query', ['*']) t.matchSnapshot(joinedOutput(), 'should return valid response with only lock info') }) t.test('with package lock and workspace', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { config: { 'package-lock-only': true, workspace: ['a'], }, prefixDir: { 'package.json': JSON.stringify({ name: 'root', workspaces: ['packages/*'], }), 'package-lock.json': JSON.stringify({ name: 'root', lockfileVersion: 3, requires: true, packages: { '': { name: 'root', workspaces: ['packages/*'], }, 'node_modules/a': { resolved: 'packages/a', link: true, }, 'packages/a': { name: 'a', version: '1.0.0', }, }, }), packages: { a: { 'package.json': JSON.stringify({ name: 'a', version: '1.0.0', }), }, }, }, }) await npm.exec('query', ['*']) t.matchSnapshot(joinedOutput(), 'should return workspace object with package-lock-only') }) t.test('no package lock and workspace', async t => { const { npm } = await loadMockNpm(t, { config: { 'package-lock-only': true, workspace: ['a'], }, prefixDir: { 'package.json': JSON.stringify({ name: 'root', workspaces: ['packages/*'], }), packages: { a: { 'package.json': JSON.stringify({ name: 'a', version: '1.0.0', }), }, }, }, }) await t.rejects(npm.exec('query', ['*']), { code: 'EUSAGE' }) }) t.test('missing workspace in tree', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { config: { workspace: ['a'], }, prefixDir: { 'package.json': JSON.stringify({ name: 'root', workspaces: ['packages/*'], }), packages: { a: { 'package.json': JSON.stringify({ name: 'a', version: '1.0.0', }), }, }, }, }) await npm.exec('query', ['*']) t.matchSnapshot(joinedOutput(), 'should return empty array for missing workspace') }) t.end() }) t.test('expect entries', t => { const { exitCode } = process t.afterEach(() => process.exitCode = exitCode) const prefixDir = { node_modules: { a: { name: 'a', version: '1.0.0' }, }, 'package.json': JSON.stringify({ name: 'project', dependencies: { a: '^1.0.0' }, }), } t.test('false, has entries', async t => { const { logs, npm, joinedOutput } = await loadMockNpm(t, { prefixDir, }) npm.config.set('expect-results', false) await npm.exec('query', ['#a']) t.not(joinedOutput(), '[]', 'has entries') t.same(logs.warn.byTitle('query'), ['query Expected no results, got 1']) t.ok(process.exitCode, 'exits with code') }) t.test('false, no entries', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { prefixDir, }) npm.config.set('expect-results', false) await npm.exec('query', ['#b']) t.equal(joinedOutput(), '[]', 'does not have entries') t.notOk(process.exitCode, 'exits without code') }) t.test('true, has entries', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { prefixDir, }) npm.config.set('expect-results', true) await npm.exec('query', ['#a']) t.not(joinedOutput(), '[]', 'has entries') t.notOk(process.exitCode, 'exits without code') }) t.test('true, no entries', async t => { const { logs, npm, joinedOutput } = await loadMockNpm(t, { prefixDir, }) npm.config.set('expect-results', true) await npm.exec('query', ['#b']) t.equal(joinedOutput(), '[]', 'does not have entries') t.same(logs.warn.byTitle('query'), ['query Expected results, got 0']) t.ok(process.exitCode, 'exits with code') }) t.test('count, matches', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { prefixDir, }) npm.config.set('expect-result-count', 1) await npm.exec('query', ['#a']) t.not(joinedOutput(), '[]', 'has entries') t.notOk(process.exitCode, 'exits without code') }) t.test('count 1, does not match', async t => { const { logs, npm, joinedOutput } = await loadMockNpm(t, { prefixDir, }) npm.config.set('expect-result-count', 1) await npm.exec('query', ['#b']) t.equal(joinedOutput(), '[]', 'does not have entries') t.same(logs.warn.byTitle('query'), ['query Expected 1 result, got 0']) t.ok(process.exitCode, 'exits with code') }) t.test('count 3, does not match', async t => { const { logs, npm, joinedOutput } = await loadMockNpm(t, { prefixDir, }) npm.config.set('expect-result-count', 3) await npm.exec('query', ['#b']) t.equal(joinedOutput(), '[]', 'does not have entries') t.same(logs.warn.byTitle('query'), ['query Expected 3 results, got 0']) t.ok(process.exitCode, 'exits with code') }) t.end() }) t.test('missing', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { prefixDir: { node_modules: { a: { name: 'a', version: '1.0.0', }, }, 'package.json': JSON.stringify({ name: 'project', dependencies: { a: '^1.0.0', b: '^1.0.0', }, }), }, }) await npm.exec('query', [':missing']) t.matchSnapshot(joinedOutput(), 'should return missing node') }) t.test('linked strategy surfaces undeclared workspaces', async t => { // npm/cli#9618: undeclared workspaces are not symlinked into root node_modules under linked, but must remain visible to `npm query`. const { npm, joinedOutput } = await loadMockNpm(t, { config: { 'install-strategy': 'linked' }, prefixDir: { 'package.json': JSON.stringify({ name: 'root', version: '1.0.0', workspaces: ['packages/*'], }), packages: { a: { 'package.json': JSON.stringify({ name: 'a', version: '1.0.0' }) }, b: { 'package.json': JSON.stringify({ name: 'b', version: '1.0.0' }) }, }, }, }) await npm.exec('query', [':root > *']) const names = JSON.parse(joinedOutput()).map(n => n.name).sort() t.same(names, ['a', 'b'], 'both undeclared workspaces are listed') })