@vltpkg/query 1.0.0-rc.32 → 1.0.0-rc.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +10 -6
- package/dist/parser.js +2 -1
- package/dist/pseudo/hostname.js +3 -1
- package/dist/pseudo/malware.d.ts +3 -16
- package/dist/pseudo/malware.js +11 -171
- package/dist/pseudo/scripts.js +1 -1
- package/dist/pseudo/vuln.d.ts +22 -0
- package/dist/pseudo/vuln.js +220 -0
- package/dist/pseudo.js +3 -3
- package/dist/types.d.ts +3 -2
- package/package.json +19 -10
- package/skills/dss-query/REFERENCE.md +123 -0
- package/skills/dss-query/SKILL.md +190 -0
- package/skills/dss-query/evals/README.md +79 -0
- package/skills/dss-query/evals/evals.json +89 -0
- package/skills/dss-query/evals/grade.mjs +480 -0
- package/src/attribute.ts +186 -0
- package/src/combinator.ts +132 -0
- package/src/id.ts +46 -0
- package/src/index.ts +620 -0
- package/src/parser.ts +122 -0
- package/src/pseudo/abandoned.ts +9 -0
- package/src/pseudo/attr.ts +92 -0
- package/src/pseudo/built.ts +19 -0
- package/src/pseudo/confused.ts +29 -0
- package/src/pseudo/cve.ts +93 -0
- package/src/pseudo/cwe.ts +97 -0
- package/src/pseudo/debug.ts +9 -0
- package/src/pseudo/deprecated.ts +9 -0
- package/src/pseudo/dev.ts +18 -0
- package/src/pseudo/diff.ts +90 -0
- package/src/pseudo/dist.ts +144 -0
- package/src/pseudo/dynamic.ts +9 -0
- package/src/pseudo/empty.ts +15 -0
- package/src/pseudo/entropic.ts +9 -0
- package/src/pseudo/env.ts +6 -0
- package/src/pseudo/eval.ts +9 -0
- package/src/pseudo/fs.ts +9 -0
- package/src/pseudo/helpers.ts +106 -0
- package/src/pseudo/host.ts +111 -0
- package/src/pseudo/hostname.ts +156 -0
- package/src/pseudo/license.ts +134 -0
- package/src/pseudo/link.ts +30 -0
- package/src/pseudo/malware.ts +41 -0
- package/src/pseudo/minified.ts +9 -0
- package/src/pseudo/missing.ts +16 -0
- package/src/pseudo/native.ts +9 -0
- package/src/pseudo/network.ts +9 -0
- package/src/pseudo/obfuscated.ts +9 -0
- package/src/pseudo/optional.ts +18 -0
- package/src/pseudo/outdated.ts +305 -0
- package/src/pseudo/overridden.ts +20 -0
- package/src/pseudo/path.ts +146 -0
- package/src/pseudo/peer.ts +18 -0
- package/src/pseudo/prerelease.ts +47 -0
- package/src/pseudo/private.ts +19 -0
- package/src/pseudo/prod.ts +18 -0
- package/src/pseudo/published.ts +248 -0
- package/src/pseudo/registry.ts +29 -0
- package/src/pseudo/root.ts +19 -0
- package/src/pseudo/scanned.ts +20 -0
- package/src/pseudo/score.ts +186 -0
- package/src/pseudo/scripts.ts +54 -0
- package/src/pseudo/semver.ts +320 -0
- package/src/pseudo/severity.ts +231 -0
- package/src/pseudo/shell.ts +9 -0
- package/src/pseudo/shrinkwrap.ts +9 -0
- package/src/pseudo/spec.ts +131 -0
- package/src/pseudo/squat.ts +225 -0
- package/src/pseudo/suspicious.ts +9 -0
- package/src/pseudo/tracker.ts +9 -0
- package/src/pseudo/trivial.ts +9 -0
- package/src/pseudo/type.ts +26 -0
- package/src/pseudo/undesirable.ts +9 -0
- package/src/pseudo/unknown.ts +9 -0
- package/src/pseudo/unmaintained.ts +9 -0
- package/src/pseudo/unpopular.ts +9 -0
- package/src/pseudo/unstable.ts +9 -0
- package/src/pseudo/vuln.ts +273 -0
- package/src/pseudo/workspace.ts +22 -0
- package/src/pseudo.ts +413 -0
- package/src/types.ts +165 -0
- package/tap-snapshots/test/attribute.ts.test.cjs +304 -0
- package/tap-snapshots/test/combinator.ts.test.cjs +333 -0
- package/tap-snapshots/test/id.ts.test.cjs +118 -0
- package/tap-snapshots/test/parser.ts.test.cjs +344 -0
- package/tap-snapshots/test/pseudo/abandoned.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/attr.ts.test.cjs +161 -0
- package/tap-snapshots/test/pseudo/built.ts.test.cjs +70 -0
- package/tap-snapshots/test/pseudo/confused.ts.test.cjs +32 -0
- package/tap-snapshots/test/pseudo/cve.ts.test.cjs +49 -0
- package/tap-snapshots/test/pseudo/cwe.ts.test.cjs +49 -0
- package/tap-snapshots/test/pseudo/debug.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/deprecated.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/dist.ts.test.cjs +30 -0
- package/tap-snapshots/test/pseudo/dynamic.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/empty.ts.test.cjs +50 -0
- package/tap-snapshots/test/pseudo/entropic.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/env.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/eval.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/fs.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/helpers.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/hostname.ts.test.cjs +148 -0
- package/tap-snapshots/test/pseudo/license.ts.test.cjs +41 -0
- package/tap-snapshots/test/pseudo/link.ts.test.cjs +36 -0
- package/tap-snapshots/test/pseudo/malware.ts.test.cjs +22 -0
- package/tap-snapshots/test/pseudo/minified.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/missing.ts.test.cjs +30 -0
- package/tap-snapshots/test/pseudo/native.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/network.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/obfuscated.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/outdated.ts.test.cjs +130 -0
- package/tap-snapshots/test/pseudo/overridden.ts.test.cjs +75 -0
- package/tap-snapshots/test/pseudo/prerelease.ts.test.cjs +101 -0
- package/tap-snapshots/test/pseudo/private.ts.test.cjs +38 -0
- package/tap-snapshots/test/pseudo/published.ts.test.cjs +171 -0
- package/tap-snapshots/test/pseudo/registry.ts.test.cjs +68 -0
- package/tap-snapshots/test/pseudo/root.ts.test.cjs +24 -0
- package/tap-snapshots/test/pseudo/scanned.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/score.ts.test.cjs +239 -0
- package/tap-snapshots/test/pseudo/scripts.ts.test.cjs +61 -0
- package/tap-snapshots/test/pseudo/semver.ts.test.cjs +575 -0
- package/tap-snapshots/test/pseudo/severity.ts.test.cjs +95 -0
- package/tap-snapshots/test/pseudo/shell.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/shrinkwrap.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/spec.ts.test.cjs +148 -0
- package/tap-snapshots/test/pseudo/squat.ts.test.cjs +129 -0
- package/tap-snapshots/test/pseudo/suspicious.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/tracker.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/trivial.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/type.ts.test.cjs +51 -0
- package/tap-snapshots/test/pseudo/undesirable.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/unknown.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/unmaintained.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/unpopular.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/unstable.ts.test.cjs +18 -0
- package/tap-snapshots/test/pseudo/vuln.ts.test.cjs +208 -0
- package/tap-snapshots/test/pseudo/vulnerable.ts.test.cjs +20 -0
- package/tap-snapshots/test/pseudo/workspace.ts.test.cjs +19 -0
- package/tap-snapshots/test/pseudo.ts.test.cjs +1247 -0
- package/test/attribute.ts +266 -0
- package/test/combinator.ts +168 -0
- package/test/fixtures/graph.ts +999 -0
- package/test/fixtures/selector.ts +103 -0
- package/test/fixtures/types.ts +7 -0
- package/test/id.ts +102 -0
- package/test/index.ts +1000 -0
- package/test/parser.ts +117 -0
- package/test/pseudo/abandoned.ts +98 -0
- package/test/pseudo/attr.ts +298 -0
- package/test/pseudo/built.ts +214 -0
- package/test/pseudo/confused.ts +160 -0
- package/test/pseudo/cve.ts +249 -0
- package/test/pseudo/cwe.ts +255 -0
- package/test/pseudo/debug.ts +98 -0
- package/test/pseudo/deprecated.ts +98 -0
- package/test/pseudo/dev.ts +61 -0
- package/test/pseudo/diff.ts +417 -0
- package/test/pseudo/dist.ts +215 -0
- package/test/pseudo/dynamic.ts +98 -0
- package/test/pseudo/empty.ts +108 -0
- package/test/pseudo/entropic.ts +101 -0
- package/test/pseudo/env.ts +98 -0
- package/test/pseudo/eval.ts +98 -0
- package/test/pseudo/fs.ts +98 -0
- package/test/pseudo/helpers.ts +370 -0
- package/test/pseudo/host-context.ts +276 -0
- package/test/pseudo/hostname.ts +314 -0
- package/test/pseudo/license.ts +265 -0
- package/test/pseudo/link.ts +94 -0
- package/test/pseudo/malware.ts +184 -0
- package/test/pseudo/minified.ts +98 -0
- package/test/pseudo/missing.ts +111 -0
- package/test/pseudo/native.ts +98 -0
- package/test/pseudo/network.ts +98 -0
- package/test/pseudo/obfuscated.ts +98 -0
- package/test/pseudo/optional.ts +73 -0
- package/test/pseudo/outdated.ts +331 -0
- package/test/pseudo/overridden.ts +317 -0
- package/test/pseudo/path.ts +680 -0
- package/test/pseudo/peer.ts +101 -0
- package/test/pseudo/prerelease.ts +279 -0
- package/test/pseudo/private.ts +108 -0
- package/test/pseudo/prod.ts +61 -0
- package/test/pseudo/published.ts +557 -0
- package/test/pseudo/registry.ts +122 -0
- package/test/pseudo/root.ts +66 -0
- package/test/pseudo/scanned.ts +78 -0
- package/test/pseudo/score.ts +591 -0
- package/test/pseudo/scripts.ts +294 -0
- package/test/pseudo/semver.ts +822 -0
- package/test/pseudo/severity.ts +310 -0
- package/test/pseudo/shell.ts +98 -0
- package/test/pseudo/shrinkwrap.ts +98 -0
- package/test/pseudo/spec.ts +525 -0
- package/test/pseudo/squat.ts +565 -0
- package/test/pseudo/suspicious.ts +98 -0
- package/test/pseudo/tracker.ts +98 -0
- package/test/pseudo/trivial.ts +98 -0
- package/test/pseudo/type.ts +105 -0
- package/test/pseudo/undesirable.ts +98 -0
- package/test/pseudo/unknown.ts +98 -0
- package/test/pseudo/unmaintained.ts +98 -0
- package/test/pseudo/unpopular.ts +98 -0
- package/test/pseudo/unstable.ts +98 -0
- package/test/pseudo/vuln.ts +625 -0
- package/test/pseudo/vulnerable.ts +137 -0
- package/test/pseudo/workspace.ts +111 -0
- package/test/pseudo.ts +558 -0
- package/tsconfig.json +3 -0
- package/tsconfig.publish.json +19 -0
- package/tsconfig.publish.tsbuildinfo +1 -0
- package/typedoc.mjs +2 -0
- package/dist/pseudo/vulnerable.d.ts +0 -8
- package/dist/pseudo/vulnerable.js +0 -17
package/test/pseudo.ts
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
import t from 'tap'
|
|
2
|
+
import { joinDepIDTuple } from '@vltpkg/dep-id'
|
|
3
|
+
import { pseudo } from '../src/pseudo.ts'
|
|
4
|
+
import {
|
|
5
|
+
getCycleGraph,
|
|
6
|
+
getMissingManifestsGraph,
|
|
7
|
+
getMissingNodeGraph,
|
|
8
|
+
getMultiWorkspaceGraph,
|
|
9
|
+
getSimpleGraph,
|
|
10
|
+
getSingleWorkspaceGraph,
|
|
11
|
+
getPathBasedGraph,
|
|
12
|
+
} from './fixtures/graph.ts'
|
|
13
|
+
import {
|
|
14
|
+
copyGraphSelectionState,
|
|
15
|
+
getGraphSelectionState,
|
|
16
|
+
selectorFixture,
|
|
17
|
+
} from './fixtures/selector.ts'
|
|
18
|
+
import type { PostcssNodeWithChildren } from '@vltpkg/dss-parser'
|
|
19
|
+
import type { TestCase } from './fixtures/types.ts'
|
|
20
|
+
import type {
|
|
21
|
+
GraphSelectionState,
|
|
22
|
+
ParserState,
|
|
23
|
+
} from '../src/types.ts'
|
|
24
|
+
|
|
25
|
+
const testPseudo = selectorFixture(pseudo)
|
|
26
|
+
|
|
27
|
+
t.test('pseudo', async t => {
|
|
28
|
+
const simpleGraph = getSimpleGraph()
|
|
29
|
+
const all = {
|
|
30
|
+
edges: new Set(simpleGraph.edges),
|
|
31
|
+
nodes: new Set(simpleGraph.nodes.values()),
|
|
32
|
+
}
|
|
33
|
+
const a = getGraphSelectionState(simpleGraph, 'a')
|
|
34
|
+
const b = getGraphSelectionState(simpleGraph, 'b')
|
|
35
|
+
const d = getGraphSelectionState(simpleGraph, 'd')
|
|
36
|
+
const empty: GraphSelectionState = {
|
|
37
|
+
edges: new Set(),
|
|
38
|
+
nodes: new Set(),
|
|
39
|
+
}
|
|
40
|
+
const queryToExpected = new Set<TestCase>([
|
|
41
|
+
[':root', all, ['my-project']], // from initial nodes
|
|
42
|
+
[':root', empty, []], // from empty nodes
|
|
43
|
+
[':root', b, []], // from diff node
|
|
44
|
+
[':project /*all*/', all, ['my-project']], // no workspaces in graph
|
|
45
|
+
[':project /*empty*/', empty, []], // from empty nodes
|
|
46
|
+
[':project /*b*/', b, []], // from diff node
|
|
47
|
+
[':scope', all, ['my-project']], // :scope refers to the initial values from initial
|
|
48
|
+
[':scope', b, []], // :scope resets the initial values from a partial
|
|
49
|
+
[':scope', empty, []], // :scope resets the initial values from empty partial
|
|
50
|
+
// :attr selector
|
|
51
|
+
[':attr([scripts])', all, ['b']], // prop lookup only
|
|
52
|
+
[':attr([scripts])', b, ['b']], // start with a single node
|
|
53
|
+
[':attr([scripts])', a, []], // unmatching single node
|
|
54
|
+
[':attr([scripts])', empty, []], // starting from empty
|
|
55
|
+
[':attr([bolinha])', all, []], // missing property
|
|
56
|
+
[':attr(a, e, [bolinha])', all, []], // fail to match something in an array of strings
|
|
57
|
+
[':attr(a, e, [bar])', all, []], // attr key can not match array items
|
|
58
|
+
[':attr(a, [e])', all, ['d']], // match something in an array of strings
|
|
59
|
+
[':attr(a, [e=bar])', all, ['d']], // match something in an array of strings
|
|
60
|
+
[':attr(a, [e=foo])', all, ['d']], // match something in an array of strings
|
|
61
|
+
[':attr([keywords=something])', all, ['c']], // match something in an array of strings
|
|
62
|
+
[':attr([keywords=missing])', all, []], // can not match missing item in array
|
|
63
|
+
[':attr(a, [e=baz])', all, []], // match something missing in an array of strings
|
|
64
|
+
[':attr(foo, bar, [baz])', all, []], // multiple missing properties
|
|
65
|
+
[':attr(scripts, [baz])', all, []], // mixed missing properties
|
|
66
|
+
[':attr(scripts, bar, [baz])', all, []], // mixed missing properties
|
|
67
|
+
[':attr(scripts, [postinstall])', all, ['b']], // nested prop
|
|
68
|
+
[':attr(scripts, [test=test])', all, ['b']], // nested prop match
|
|
69
|
+
[':attr(contributors, [name^=Ruy])', all, ['b']], // nested array
|
|
70
|
+
[
|
|
71
|
+
':attr(contributors, [email=ruyadorno@example.com])',
|
|
72
|
+
all,
|
|
73
|
+
['b'],
|
|
74
|
+
],
|
|
75
|
+
[':attr([peerDependenciesMeta])', all, ['c']],
|
|
76
|
+
[':attr(peerDependenciesMeta, [foo])', all, ['c']],
|
|
77
|
+
[':attr(peerDependenciesMeta, foo, [optional])', all, ['c']], // 2-lvls
|
|
78
|
+
[':attr(peerDependenciesMeta, foo, [optional=true])', all, ['c']],
|
|
79
|
+
[':attr([a])', all, ['d']], // nested props start
|
|
80
|
+
[':attr(a, [b])', all, ['d']], // nested props
|
|
81
|
+
[':attr(a, b, [c])', all, ['d']], // nested props within array
|
|
82
|
+
[':attr(a, b, c, [d])', all, ['d']], // nested props within array
|
|
83
|
+
[':attr(a, b, c, [d=foo])', all, ['d']], // nested props within array
|
|
84
|
+
[':attr(a, b, c, [d=bar])', all, ['d']], // nested props within array
|
|
85
|
+
[
|
|
86
|
+
':attr([name])',
|
|
87
|
+
all,
|
|
88
|
+
['my-project', 'a', 'b', 'c', 'd', 'e', 'f', '@x/y'],
|
|
89
|
+
], // nested props within array
|
|
90
|
+
[
|
|
91
|
+
':attr([version^=1])',
|
|
92
|
+
all,
|
|
93
|
+
['my-project', 'a', 'c', 'd', 'e', 'f', '@x/y'],
|
|
94
|
+
], // nested props within array
|
|
95
|
+
[':empty', all, ['a', 'c', 'e', 'f', '@x/y']], // deps with no deps
|
|
96
|
+
[':empty', a, ['a']], // single :empty item
|
|
97
|
+
[':empty', b, []], // single not :empty item
|
|
98
|
+
[':empty', empty, []], // no items to filter
|
|
99
|
+
[':has(*)', all, ['my-project', 'b', 'd']], // has any sub selector matches, e.g: children
|
|
100
|
+
[':has(*)', b, ['b']], // has any sub selector matches, e.g: children
|
|
101
|
+
[':has(> *)', all, ['my-project', 'b', 'd']], // has any sub selector matches, e.g: children
|
|
102
|
+
[':has(> *)', b, ['b']], // has any sub selector matches, e.g: children
|
|
103
|
+
[':has([name=e])', all, ['my-project', 'd']], // has any sub selector matches, e.g: children
|
|
104
|
+
[':has(* [name=e])', all, ['my-project', 'd']], // has any sub selector matches, e.g: children
|
|
105
|
+
[':has(* > [name=e])', all, ['my-project', 'd']], // has any sub selector matches, e.g: children
|
|
106
|
+
[':has(* > * > [name=f])', all, ['d']], // has any sub selector matches, e.g: children
|
|
107
|
+
[':has([name=c], [name=f])', all, ['b', 'd']], // multi sub selectors
|
|
108
|
+
[':has([name=c], [name=f])', b, ['b']], // multi sub selectors, selecting from single item
|
|
109
|
+
[':has([name=c], [name=f])', empty, []], // multi sub selectors, selecting from no item
|
|
110
|
+
[':has(*[nonexistingattribute])', all, []], // nothing to match
|
|
111
|
+
[':has(~ :attr([peerDependenciesMeta]))', all, ['b']], // has any sub selector matches, e.g: children
|
|
112
|
+
[':has(:attr([scripts]))', all, ['my-project']], // has child prop lookup
|
|
113
|
+
[':has(:type(workspace))', all, []], // has no result
|
|
114
|
+
[':is(:root)', all, ['my-project']], // can match a single node
|
|
115
|
+
[':is([name=a], [name=b], [name=f])', all, ['a', 'b', 'f']], // can match multiple nodes
|
|
116
|
+
[':is(:root)', empty, []], // can't match from empty partial
|
|
117
|
+
[
|
|
118
|
+
':is(#foo, [name===z], :root +, :nonexistingselector)',
|
|
119
|
+
all,
|
|
120
|
+
['my-project', 'a', 'b', 'c', 'd', 'e', 'f', '@x/y'],
|
|
121
|
+
], // ignore any invalid selectors on loose mode
|
|
122
|
+
[':is(:foo)', all, []], // broken selectors fail to match items when in loose mode
|
|
123
|
+
[':is([name=a], [name=b], [name=f])', empty, []], // can match multiple nodes if no partial
|
|
124
|
+
[':missing', all, []], // no dangling edges in this graph
|
|
125
|
+
[':not(:root)', all, ['a', 'b', 'c', 'd', 'e', 'f', '@x/y']], // can negate single node
|
|
126
|
+
[
|
|
127
|
+
':not([name=a], [name=b], [name=f])',
|
|
128
|
+
all,
|
|
129
|
+
['my-project', 'c', 'd', 'e', '@x/y'],
|
|
130
|
+
], // can negate multiple selectors
|
|
131
|
+
[':not(:root)', empty, []], // can't negate from empty partial
|
|
132
|
+
[':not([name=a], [name=b], [name=f])', empty, []], // can negate multiple nodes if no partial
|
|
133
|
+
[':private', all, ['d']], // private dep
|
|
134
|
+
[':private', d, ['d']], // single :private item
|
|
135
|
+
[':private', b, []], // single not :private item
|
|
136
|
+
[':private', empty, []], // no items to filter
|
|
137
|
+
[':semver(^2)', all, ['b']], // semver selector
|
|
138
|
+
[':semver(=2.0.0)', all, ['b']], // semver selector
|
|
139
|
+
[
|
|
140
|
+
':semver("<2.0.0")',
|
|
141
|
+
all,
|
|
142
|
+
['my-project', 'a', 'c', 'd', 'e', 'f', '@x/y'],
|
|
143
|
+
], // quoted semver selector
|
|
144
|
+
[':type(file)', all, ['my-project', '@x/y']], // type selector
|
|
145
|
+
[':type(git)', all, []], // type selector, no matches
|
|
146
|
+
[':type(registry)', all, ['a', 'b', 'c', 'd', 'e', 'f']], // type selector, no matches
|
|
147
|
+
[':type(registry)', empty, []], // type selector, nothing to match
|
|
148
|
+
[':dev', all, ['b', '@x/y']], // select dev deps
|
|
149
|
+
[':dev', b, ['b']], // single item dev dep
|
|
150
|
+
[':dev', empty, []], // empty starting partial
|
|
151
|
+
])
|
|
152
|
+
const initial = copyGraphSelectionState(all)
|
|
153
|
+
for (const [query, partial, expected] of queryToExpected) {
|
|
154
|
+
const result = await testPseudo(
|
|
155
|
+
query,
|
|
156
|
+
initial,
|
|
157
|
+
copyGraphSelectionState(partial),
|
|
158
|
+
)
|
|
159
|
+
t.strictSame(
|
|
160
|
+
result.nodes.map(i => i.name),
|
|
161
|
+
expected,
|
|
162
|
+
`query > "${query}"`,
|
|
163
|
+
)
|
|
164
|
+
t.matchSnapshot(
|
|
165
|
+
{
|
|
166
|
+
edges: result.edges.map(i => i.name).sort(),
|
|
167
|
+
nodes: result.nodes.map(i => i.name).sort(),
|
|
168
|
+
},
|
|
169
|
+
`query > "${query}"`,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
await t.test('workspace', async t => {
|
|
174
|
+
const wsGraph = getSingleWorkspaceGraph()
|
|
175
|
+
const all: GraphSelectionState = {
|
|
176
|
+
edges: new Set(wsGraph.edges),
|
|
177
|
+
nodes: new Set(wsGraph.nodes.values()),
|
|
178
|
+
}
|
|
179
|
+
const w: GraphSelectionState = {
|
|
180
|
+
edges: new Set(),
|
|
181
|
+
nodes: new Set([
|
|
182
|
+
wsGraph.nodes.get(joinDepIDTuple(['workspace', 'w']))!,
|
|
183
|
+
]),
|
|
184
|
+
}
|
|
185
|
+
const empty: GraphSelectionState = {
|
|
186
|
+
edges: new Set(),
|
|
187
|
+
nodes: new Set(),
|
|
188
|
+
}
|
|
189
|
+
const queryToExpected = new Set<TestCase>([
|
|
190
|
+
[':root', all, ['ws']], // root
|
|
191
|
+
[':project /*all*/', all, ['ws', 'w']], // project = root & workspaces
|
|
192
|
+
[':project /*empty*/', empty, []], // from empty nodes
|
|
193
|
+
[':project /*w*/', w, ['w']], // from single node
|
|
194
|
+
[':empty', all, ['ws', 'w']], // deps with no deps
|
|
195
|
+
[':is(:workspace, :root)', all, ['ws', 'w']],
|
|
196
|
+
[':is(:workspace, :root)', empty, []],
|
|
197
|
+
[':is(:workspace)', all, ['w']],
|
|
198
|
+
[':is(:workspace)', empty, []],
|
|
199
|
+
[':not(:workspace, :root)', all, []], // excludes all items
|
|
200
|
+
[':not(:workspace)', all, ['ws']], // excludes workspaces
|
|
201
|
+
[':not(:root)', all, ['w']], // excludes root
|
|
202
|
+
[':not(:root)', empty, []], // empty starting partial
|
|
203
|
+
[':private', all, []], // private dep
|
|
204
|
+
[':type(registry)', all, []], // type selector
|
|
205
|
+
[':type(workspace)', all, ['w']], // type selector workspace
|
|
206
|
+
[':type(file)', all, ['ws']], // type selector root
|
|
207
|
+
[':dev', all, []], // no dev deps in this graph
|
|
208
|
+
[':dev', empty, []], // empty starting partial
|
|
209
|
+
])
|
|
210
|
+
const initial = copyGraphSelectionState(all)
|
|
211
|
+
for (const [query, partial, expected] of queryToExpected) {
|
|
212
|
+
const result = await testPseudo(
|
|
213
|
+
query,
|
|
214
|
+
initial,
|
|
215
|
+
copyGraphSelectionState(partial),
|
|
216
|
+
)
|
|
217
|
+
t.strictSame(
|
|
218
|
+
result.nodes.map(i => i.name),
|
|
219
|
+
expected,
|
|
220
|
+
`query > "${query}"`,
|
|
221
|
+
)
|
|
222
|
+
t.matchSnapshot(
|
|
223
|
+
{
|
|
224
|
+
edges: result.edges.map(i => i.name).sort(),
|
|
225
|
+
nodes: result.nodes.map(i => i.name).sort(),
|
|
226
|
+
},
|
|
227
|
+
`query > "${query}"`,
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// test custom scopedID
|
|
232
|
+
const result = await testPseudo(
|
|
233
|
+
':scope',
|
|
234
|
+
initial,
|
|
235
|
+
copyGraphSelectionState(all),
|
|
236
|
+
false,
|
|
237
|
+
[joinDepIDTuple(['workspace', 'w'])],
|
|
238
|
+
)
|
|
239
|
+
t.strictSame(
|
|
240
|
+
result.nodes.map(i => i.name),
|
|
241
|
+
['w'],
|
|
242
|
+
'query > customScopedID',
|
|
243
|
+
)
|
|
244
|
+
t.matchSnapshot(
|
|
245
|
+
{
|
|
246
|
+
edges: result.edges.map(i => i.name).sort(),
|
|
247
|
+
nodes: result.nodes.map(i => i.name).sort(),
|
|
248
|
+
},
|
|
249
|
+
'query > customScopedID',
|
|
250
|
+
)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
await t.test('complex workspace', async t => {
|
|
254
|
+
const wsGraph = getMultiWorkspaceGraph()
|
|
255
|
+
const all: GraphSelectionState = {
|
|
256
|
+
edges: new Set(wsGraph.edges),
|
|
257
|
+
nodes: new Set(wsGraph.nodes.values()),
|
|
258
|
+
}
|
|
259
|
+
//const w = getGraphSelectionState(wsGraph, 'w')
|
|
260
|
+
const queryToExpected = new Set<TestCase>([
|
|
261
|
+
[':root', all, ['ws']], // root
|
|
262
|
+
[':project', all, ['ws', 'a', 'b', 'c']], // project = root & workspaces
|
|
263
|
+
])
|
|
264
|
+
const initial = copyGraphSelectionState(all)
|
|
265
|
+
for (const [query, partial, expected] of queryToExpected) {
|
|
266
|
+
const result = await testPseudo(
|
|
267
|
+
query,
|
|
268
|
+
initial,
|
|
269
|
+
copyGraphSelectionState(partial),
|
|
270
|
+
)
|
|
271
|
+
t.strictSame(
|
|
272
|
+
result.nodes.map(i => i.name),
|
|
273
|
+
expected,
|
|
274
|
+
`query > "${query}"`,
|
|
275
|
+
)
|
|
276
|
+
t.matchSnapshot(
|
|
277
|
+
{
|
|
278
|
+
edges: result.edges.map(i => i.name).sort(),
|
|
279
|
+
nodes: result.nodes.map(i => i.name).sort(),
|
|
280
|
+
},
|
|
281
|
+
`query > "${query}"`,
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
await t.test('cycle', async t => {
|
|
287
|
+
const cycleGraph = getCycleGraph()
|
|
288
|
+
const all: GraphSelectionState = {
|
|
289
|
+
edges: new Set(cycleGraph.edges),
|
|
290
|
+
nodes: new Set(cycleGraph.nodes.values()),
|
|
291
|
+
}
|
|
292
|
+
const queryToExpected = new Set<TestCase>([
|
|
293
|
+
[':root', all, ['cycle-project']], // root
|
|
294
|
+
[':project', all, ['cycle-project']], // project = root & workspaces
|
|
295
|
+
[':attr([scripts])', all, ['a', 'b']], // any pkgs with scripts
|
|
296
|
+
[':attr(scripts, [test=foo])', all, ['a']], // any pkgs with scripts
|
|
297
|
+
[':empty', all, []], // no deps with no deps
|
|
298
|
+
[':is([name=a])', all, ['a']],
|
|
299
|
+
[':private', all, []], // private dep
|
|
300
|
+
[':type(registry)', all, ['a', 'b']], // type selector
|
|
301
|
+
[':dev', all, []], // no dev deps in this graph
|
|
302
|
+
])
|
|
303
|
+
const initial = copyGraphSelectionState(all)
|
|
304
|
+
for (const [query, partial, expected] of queryToExpected) {
|
|
305
|
+
const result = await testPseudo(
|
|
306
|
+
query,
|
|
307
|
+
initial,
|
|
308
|
+
copyGraphSelectionState(partial),
|
|
309
|
+
)
|
|
310
|
+
t.strictSame(
|
|
311
|
+
result.nodes.map(i => i.name),
|
|
312
|
+
expected,
|
|
313
|
+
`query > "${query}"`,
|
|
314
|
+
)
|
|
315
|
+
t.matchSnapshot(
|
|
316
|
+
{
|
|
317
|
+
edges: result.edges.map(i => i.name).sort(),
|
|
318
|
+
nodes: result.nodes.map(i => i.name).sort(),
|
|
319
|
+
},
|
|
320
|
+
`query > "${query}"`,
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
await t.test('missing manifest', async t => {
|
|
326
|
+
const mmGraph = getMissingManifestsGraph()
|
|
327
|
+
const all: GraphSelectionState = {
|
|
328
|
+
edges: new Set(mmGraph.edges),
|
|
329
|
+
nodes: new Set(mmGraph.nodes.values()),
|
|
330
|
+
}
|
|
331
|
+
const queryToExpected = new Set<TestCase>([
|
|
332
|
+
[':attr([scripts])', all, []],
|
|
333
|
+
[':attr(scripts, [test=foo])', all, []],
|
|
334
|
+
[':is([name=a])', all, []],
|
|
335
|
+
])
|
|
336
|
+
const initial = copyGraphSelectionState(all)
|
|
337
|
+
for (const [query, partial, expected] of queryToExpected) {
|
|
338
|
+
const result = await testPseudo(
|
|
339
|
+
query,
|
|
340
|
+
initial,
|
|
341
|
+
copyGraphSelectionState(partial),
|
|
342
|
+
)
|
|
343
|
+
t.strictSame(
|
|
344
|
+
result.nodes.map(i => i.name),
|
|
345
|
+
expected,
|
|
346
|
+
`query > "${query}"`,
|
|
347
|
+
)
|
|
348
|
+
t.matchSnapshot(
|
|
349
|
+
{
|
|
350
|
+
edges: result.edges.map(i => i.name).sort(),
|
|
351
|
+
nodes: result.nodes.map(i => i.name).sort(),
|
|
352
|
+
},
|
|
353
|
+
`query > "${query}"`,
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
await t.test('missing node', async t => {
|
|
359
|
+
const missingNodeGraph = getMissingNodeGraph()
|
|
360
|
+
const all: GraphSelectionState = {
|
|
361
|
+
edges: new Set(missingNodeGraph.edges),
|
|
362
|
+
nodes: new Set(missingNodeGraph.nodes.values()),
|
|
363
|
+
}
|
|
364
|
+
// tests on missing node cases use only snapshot-check
|
|
365
|
+
// giving that we want to assert missing / present edges
|
|
366
|
+
const queryToExpected = new Set<TestCase>([
|
|
367
|
+
[':missing', all, []],
|
|
368
|
+
[':has(:dev)', all, []],
|
|
369
|
+
[':private', all, []],
|
|
370
|
+
[':dev', all, []], // test that :dev works with missing nodes as well
|
|
371
|
+
])
|
|
372
|
+
const initial = copyGraphSelectionState(all)
|
|
373
|
+
for (const [query, partial] of queryToExpected) {
|
|
374
|
+
const result = await testPseudo(
|
|
375
|
+
query,
|
|
376
|
+
initial,
|
|
377
|
+
copyGraphSelectionState(partial),
|
|
378
|
+
)
|
|
379
|
+
t.matchSnapshot(
|
|
380
|
+
{
|
|
381
|
+
edges: result.edges.map(i => i.name).sort(),
|
|
382
|
+
nodes: result.nodes.map(i => i.name).sort(),
|
|
383
|
+
},
|
|
384
|
+
`query > "${query}"`,
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
t.test('bad selector type', async t => {
|
|
391
|
+
await t.rejects(
|
|
392
|
+
testPseudo('[name=a]'),
|
|
393
|
+
/Mismatching query node/,
|
|
394
|
+
'should throw an error',
|
|
395
|
+
)
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
t.test('unsupported pseudo', async t => {
|
|
399
|
+
await t.rejects(
|
|
400
|
+
testPseudo(':unsupportedpseudoclass'),
|
|
401
|
+
/Unsupported pseudo-class: :unsupportedpseudoclass/,
|
|
402
|
+
'should throw an unsupported selector error',
|
|
403
|
+
)
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
t.test('specificity calculation for pseudo selectors', async t => {
|
|
407
|
+
const simpleGraph = getSimpleGraph()
|
|
408
|
+
const all = {
|
|
409
|
+
edges: new Set(simpleGraph.edges),
|
|
410
|
+
nodes: new Set(simpleGraph.nodes.values()),
|
|
411
|
+
}
|
|
412
|
+
const initial = copyGraphSelectionState(all)
|
|
413
|
+
|
|
414
|
+
// Test normal pseudo selectors (should increment commonCounter)
|
|
415
|
+
const normalState: ParserState = {
|
|
416
|
+
cancellable: async () => {},
|
|
417
|
+
collect: {
|
|
418
|
+
nodes: new Set(simpleGraph.nodes.values()),
|
|
419
|
+
edges: new Set(simpleGraph.edges),
|
|
420
|
+
},
|
|
421
|
+
current: {
|
|
422
|
+
type: 'pseudo',
|
|
423
|
+
value: ':dev',
|
|
424
|
+
spaces: { before: '', after: '' },
|
|
425
|
+
} as any,
|
|
426
|
+
initial: copyGraphSelectionState(initial),
|
|
427
|
+
loose: false,
|
|
428
|
+
partial: copyGraphSelectionState(initial),
|
|
429
|
+
importers: new Set(simpleGraph.importers),
|
|
430
|
+
hostContexts: undefined,
|
|
431
|
+
retries: 0,
|
|
432
|
+
securityArchive: undefined,
|
|
433
|
+
signal: { throwIfAborted: () => {} } as AbortSignal,
|
|
434
|
+
walk: async (state: ParserState) => state,
|
|
435
|
+
specificity: { idCounter: 0, commonCounter: 0 },
|
|
436
|
+
comment: '',
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const result = await pseudo(normalState)
|
|
440
|
+
t.equal(
|
|
441
|
+
result.specificity.commonCounter,
|
|
442
|
+
1,
|
|
443
|
+
':dev should increment commonCounter',
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
// Test :not, :is, :has pseudo selectors (should not increment commonCounter)
|
|
447
|
+
const notCounter = { idCounter: 0, commonCounter: 0 }
|
|
448
|
+
const isCounter = { idCounter: 0, commonCounter: 0 }
|
|
449
|
+
const hasCounter = { idCounter: 0, commonCounter: 0 }
|
|
450
|
+
|
|
451
|
+
const notState: ParserState = {
|
|
452
|
+
...normalState,
|
|
453
|
+
current: {
|
|
454
|
+
type: 'pseudo',
|
|
455
|
+
value: ':not',
|
|
456
|
+
spaces: { before: '', after: '' },
|
|
457
|
+
nodes: [],
|
|
458
|
+
} as unknown as PostcssNodeWithChildren,
|
|
459
|
+
specificity: { ...notCounter },
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const isState: ParserState = {
|
|
463
|
+
...normalState,
|
|
464
|
+
current: {
|
|
465
|
+
type: 'pseudo',
|
|
466
|
+
value: ':is',
|
|
467
|
+
spaces: { before: '', after: '' },
|
|
468
|
+
nodes: [],
|
|
469
|
+
} as unknown as PostcssNodeWithChildren,
|
|
470
|
+
specificity: { ...isCounter },
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const hasState: ParserState = {
|
|
474
|
+
...normalState,
|
|
475
|
+
current: {
|
|
476
|
+
type: 'pseudo',
|
|
477
|
+
value: ':has',
|
|
478
|
+
spaces: { before: '', after: '' },
|
|
479
|
+
nodes: [],
|
|
480
|
+
} as unknown as PostcssNodeWithChildren,
|
|
481
|
+
specificity: { ...hasCounter },
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const notResult = await pseudo(notState)
|
|
485
|
+
const isResult = await pseudo(isState)
|
|
486
|
+
const hasResult = await pseudo(hasState)
|
|
487
|
+
|
|
488
|
+
t.equal(
|
|
489
|
+
notResult.specificity.commonCounter,
|
|
490
|
+
0,
|
|
491
|
+
':not should not increment commonCounter',
|
|
492
|
+
)
|
|
493
|
+
t.equal(
|
|
494
|
+
isResult.specificity.commonCounter,
|
|
495
|
+
0,
|
|
496
|
+
':is should not increment commonCounter',
|
|
497
|
+
)
|
|
498
|
+
t.equal(
|
|
499
|
+
hasResult.specificity.commonCounter,
|
|
500
|
+
0,
|
|
501
|
+
':has should not increment commonCounter',
|
|
502
|
+
)
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
t.test(':path selector', async t => {
|
|
506
|
+
const pathBasedGraph = getPathBasedGraph()
|
|
507
|
+
const all = {
|
|
508
|
+
edges: new Set(pathBasedGraph.edges),
|
|
509
|
+
nodes: new Set(pathBasedGraph.nodes.values()),
|
|
510
|
+
}
|
|
511
|
+
const empty: GraphSelectionState = {
|
|
512
|
+
edges: new Set(),
|
|
513
|
+
nodes: new Set(),
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const queryToExpected = new Set<TestCase>([
|
|
517
|
+
// Test basic path matching - all patterns must be quoted
|
|
518
|
+
[
|
|
519
|
+
':path("*")',
|
|
520
|
+
all,
|
|
521
|
+
['path-based-project', 'a', 'b', 'c', 'x', 'y'],
|
|
522
|
+
], // Match all workspace and file nodes
|
|
523
|
+
[':path("packages/*")', all, ['a', 'b']], // Match workspace packages in packages directory
|
|
524
|
+
[':path("packages/a")', all, ['a']], // Match specific workspace
|
|
525
|
+
[':path(".")', all, ['path-based-project']], // Match root project
|
|
526
|
+
[':path("x")', all, ['x']], // Match file dependency
|
|
527
|
+
[':path("packages/**")', all, ['a', 'b', 'y']], // Match with glob patterns
|
|
528
|
+
[':path("packages/a/*")', all, ['y']], // Match nested file paths
|
|
529
|
+
[':path("nonexistent/**")', all, []], // No matches
|
|
530
|
+
|
|
531
|
+
// Test edge cases
|
|
532
|
+
[':path("")', all, []], // Empty pattern should match nothing
|
|
533
|
+
[':path()', all, []], // Missing pattern should match nothing
|
|
534
|
+
|
|
535
|
+
// Test from empty partial
|
|
536
|
+
[':path("*")', empty, []], // Empty input should return empty
|
|
537
|
+
])
|
|
538
|
+
|
|
539
|
+
for (const [query, partial, expectedNames] of queryToExpected) {
|
|
540
|
+
try {
|
|
541
|
+
// Convert string names to DepID array
|
|
542
|
+
const expectedDepIDs = expectedNames.map(name =>
|
|
543
|
+
joinDepIDTuple(['registry', '', `${name}@1.0.0`]),
|
|
544
|
+
)
|
|
545
|
+
await testPseudo(query, all, partial, false, expectedDepIDs)
|
|
546
|
+
} catch (error) {
|
|
547
|
+
t.fail(`Failed query "${query}": ${error}`)
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
t.test('unexpected attr usage', async t => {
|
|
553
|
+
await t.rejects(
|
|
554
|
+
testPseudo(':attr', undefined, undefined, true),
|
|
555
|
+
/Failed to parse :attr selector/,
|
|
556
|
+
'should throw a failure to parse error',
|
|
557
|
+
)
|
|
558
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"src/**/*.ts",
|
|
5
|
+
"src/**/*.mts",
|
|
6
|
+
"src/**/*.tsx",
|
|
7
|
+
"src/**/*.json"
|
|
8
|
+
],
|
|
9
|
+
"exclude": ["src/package.json"],
|
|
10
|
+
"compilerOptions": {
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src",
|
|
13
|
+
"module": "nodenext",
|
|
14
|
+
"moduleResolution": "nodenext",
|
|
15
|
+
"sourceMap": false,
|
|
16
|
+
"declarationMap": false,
|
|
17
|
+
"inlineSources": false
|
|
18
|
+
}
|
|
19
|
+
}
|