Skip to content

Commit 3d49bc1

Browse files
committed
fix(plugin:nextcloud-vue): use resolved dependency for detecting nextcloud-vue version
This fixes multiple issues: - If package.json is not pinned but we already use an updated version (e.g. package-lock.json is only updated). - If the package.json is not in the root directory (e.g. mono repos etc). Signed-off-by: Ferdinand Thiessen <[email protected]>
1 parent 43a08c5 commit 3d49bc1

File tree

4 files changed

+112
-289
lines changed

4 files changed

+112
-289
lines changed

lib/plugins/nextcloud-vue/rules/no-deprecated-exports.test.ts

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,18 @@
44
*/
55

66
import { RuleTester } from 'eslint'
7-
import { fs, vol } from 'memfs'
8-
import { afterAll, beforeAll, describe, test, vi } from 'vitest'
7+
import { gte } from 'semver'
8+
import { describe, test, vi } from 'vitest'
99
import vueParser from 'vue-eslint-parser'
1010
import rule from './no-deprecated-exports.ts'
1111

12-
vi.mock('node:fs', () => fs)
12+
const createLibVersionValidator = vi.hoisted(() => vi.fn(() => (version: string) => !!version))
1313

14-
describe('no-deprecated-exports', () => {
15-
beforeAll(() => vol.fromNestedJSON({
16-
[__dirname]: {},
17-
[__filename]: '...',
18-
}))
19-
afterAll(() => vol.reset())
14+
vi.mock('../utils/lib-version-parser.ts', () => ({
15+
createLibVersionValidator,
16+
}))
2017

18+
describe('no-deprecated-exports', () => {
2119
const ruleTester = new RuleTester({
2220
languageOptions: {
2321
parser: vueParser,
@@ -26,12 +24,7 @@ describe('no-deprecated-exports', () => {
2624
})
2725

2826
test('no-deprecated-exports if library is not in use', () => {
29-
vol.fromNestedJSON({
30-
'/a': {
31-
'package.json': '{"name": "my-app","version": "0.1.0"}',
32-
src: { },
33-
},
34-
}, '/a/src')
27+
createLibVersionValidator.mockReturnValue(() => false)
3528
ruleTester.run('no-deprecated-exports', rule, {
3629
valid: [
3730
{
@@ -50,12 +43,7 @@ describe('no-deprecated-exports', () => {
5043
})
5144

5245
test('no-deprecated-exports if library has outdated version', () => {
53-
vol.fromNestedJSON({
54-
'/a': {
55-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{"@nextcloud/vue":"^8.22.0"}}',
56-
src: { },
57-
},
58-
})
46+
createLibVersionValidator.mockReturnValue((version: string) => gte('8.22.0', version))
5947
ruleTester.run('no-deprecated-exports', rule, {
6048
valid: [
6149
{
@@ -74,12 +62,7 @@ describe('no-deprecated-exports', () => {
7462
})
7563

7664
test('no-deprecated-exports for dist syntax', () => {
77-
vol.fromNestedJSON({
78-
'/a': {
79-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{"@nextcloud/vue":"^8.23.1"}}',
80-
src: { },
81-
},
82-
})
65+
createLibVersionValidator.mockReturnValue((version: string) => gte('8.23.1', version))
8366
ruleTester.run('no-deprecated-exports', rule, {
8467
valid: [
8568
{
@@ -130,12 +113,7 @@ describe('no-deprecated-exports', () => {
130113
})
131114

132115
test('no-deprecated-exports for removed content', () => {
133-
vol.fromNestedJSON({
134-
'/a': {
135-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{"@nextcloud/vue":"^8.31.0"}}',
136-
src: { },
137-
},
138-
})
116+
createLibVersionValidator.mockReturnValue((version: string) => gte('8.31.0', version))
139117
ruleTester.run('no-deprecated-exports', rule, {
140118
valid: [
141119
{

lib/plugins/nextcloud-vue/rules/no-deprecated-props.test.ts

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,18 @@
44
*/
55

66
import { RuleTester } from 'eslint'
7-
import { fs, vol } from 'memfs'
8-
import { afterAll, beforeAll, describe, test, vi } from 'vitest'
7+
import { gte } from 'semver'
8+
import { describe, test, vi } from 'vitest'
99
import vueParser from 'vue-eslint-parser'
1010
import rule from './no-deprecated-props.ts'
1111

12-
vi.mock('node:fs', () => fs)
12+
const createLibVersionValidator = vi.hoisted(() => vi.fn(() => (version: string) => !!version))
1313

14-
describe('no-deprecated-props', () => {
15-
beforeAll(() => vol.fromNestedJSON({
16-
[__dirname]: {},
17-
[__filename]: '...',
18-
}))
19-
afterAll(() => vol.reset())
14+
vi.mock('../utils/lib-version-parser.ts', () => ({
15+
createLibVersionValidator,
16+
}))
2017

18+
describe('no-deprecated-props', () => {
2119
const ruleTester = new RuleTester({
2220
languageOptions: {
2321
parser: vueParser,
@@ -26,12 +24,7 @@ describe('no-deprecated-props', () => {
2624
})
2725

2826
test('no-deprecated-props if library is not in use', () => {
29-
vol.fromNestedJSON({
30-
'/a': {
31-
'package.json': '{"name": "my-app","version": "0.1.0"}',
32-
src: { },
33-
},
34-
}, '/a/src')
27+
createLibVersionValidator.mockReturnValue(() => false)
3528
ruleTester.run('no-deprecated-props', rule, {
3629
valid: [
3730
{
@@ -50,12 +43,7 @@ describe('no-deprecated-props', () => {
5043
})
5144

5245
test('no-deprecated-props if library has outdated version', () => {
53-
vol.fromNestedJSON({
54-
'/a': {
55-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{"@nextcloud/vue":"^8.23.1"}}',
56-
src: { },
57-
},
58-
})
46+
createLibVersionValidator.mockReturnValue((version) => gte('8.23.1', version))
5947
ruleTester.run('no-deprecated-props', rule, {
6048
valid: [
6149
{
@@ -83,12 +71,7 @@ describe('no-deprecated-props', () => {
8371
})
8472

8573
test('no-deprecated-props', () => {
86-
vol.fromNestedJSON({
87-
'/a': {
88-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{"@nextcloud/vue":"^9.0.0"}}',
89-
src: { },
90-
},
91-
})
74+
createLibVersionValidator.mockReturnValue((version) => gte('9.0.0', version))
9275
ruleTester.run('no-deprecated-props', rule, {
9376
valid: [
9477
{

lib/plugins/nextcloud-vue/utils/lib-version-parser.test.ts

Lines changed: 61 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -2,176 +2,86 @@
22
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5+
56
import { fs, vol } from 'memfs'
6-
import { join } from 'node:path'
7-
import { afterAll, afterEach, beforeAll, describe, expect, it, test, vi } from 'vitest'
8-
import {
9-
createLibVersionValidator,
10-
findPackageJsonDir,
11-
isFile,
12-
sanitizeTargetVersion,
13-
} from './lib-version-parser.ts'
7+
import { beforeEach } from 'node:test'
8+
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
9+
import { clearCache, createLibVersionValidator } from './lib-version-parser.ts'
1410

1511
vi.mock('node:fs', () => fs)
1612

17-
describe('version-parser', () => {
13+
describe('createLibVersionValidator', () => {
1814
beforeAll(() => vol.fromNestedJSON({
1915
[__dirname]: {},
2016
[__filename]: '...',
2117
}))
18+
beforeEach(() => clearCache())
2219
afterAll(() => vol.reset())
2320

24-
test('isFile', () => {
25-
expect(isFile(__dirname)).toBe(false)
26-
expect(isFile(__filename)).toBe(true)
27-
expect(isFile(join(__dirname, 'does-not-exists.invalid'))).toBe(false)
28-
})
29-
30-
test('sanitizeTargetVersion', () => {
31-
expect(sanitizeTargetVersion('^23.0.0')).toBe('23.0.0')
32-
expect(sanitizeTargetVersion('25.0')).toBe('25.0.0')
33-
expect(sanitizeTargetVersion('25.0.1')).toBe('25.0.1')
34-
35-
try {
36-
const output = sanitizeTargetVersion('a.b.c')
37-
expect(output).toBe('Should not be reached')
38-
} catch (e) {
39-
expect(e.message).toMatch(/Invalid comparator/)
40-
}
41-
42-
try {
43-
const output = sanitizeTargetVersion('25.0.0.1')
44-
expect(output).toBe('Should not be reached')
45-
} catch (e) {
46-
expect(e.message).toMatch(/Invalid comparator/)
47-
}
48-
})
49-
50-
describe('findPackageJsonDir', () => {
51-
afterEach(() => vol.reset())
52-
53-
it('finds a package.json if provided', () => {
54-
vol.fromNestedJSON({
55-
'/a': {
56-
'package.json': '...',
57-
src: {},
58-
},
59-
})
60-
61-
expect(findPackageJsonDir('/a/src')).toBe('/a')
21+
it.for`
22+
version | expected
23+
------------|---------
24+
${'8.22.0'} | ${false}
25+
${'8.23.0'} | ${false}
26+
${'8.23.1'} | ${false}
27+
${'8.24.0'} | ${false}
28+
${'9.0.0'} | ${false}
29+
`('returns false without nextcloud/vue in dependencies', ({ version, expected }) => {
30+
const fn = createLibVersionValidator({
31+
cwd: '',
32+
physicalFilename: '/a/src/b.js',
33+
}, () => {
34+
throw new Error('not found', { cause: 'ERR_MODULE_NOT_FOUND' })
6235
})
36+
expect(fn(version)).toBe(expected)
37+
})
6338

64-
it('finds a package.json if provided on lower directory', () => {
65-
vol.fromNestedJSON({
66-
'/a': {
67-
'package.json': '...',
68-
src: {
69-
b: {
70-
c: {},
39+
it('works with physical filename', () => {
40+
vol.fromNestedJSON({
41+
'/a': {
42+
node_modules: {
43+
'@nextcloud': {
44+
vue: {
45+
'package.json': '{"version": "8.23.1"}',
7146
},
7247
},
7348
},
74-
})
75-
76-
expect(findPackageJsonDir('/a/src/b/c')).toBe('/a')
77-
})
78-
79-
it('returns undefined if not found', () => {
80-
vol.fromNestedJSON({
81-
'/a/src/b/c': {},
82-
})
83-
84-
expect(findPackageJsonDir('/a/src/b/c')).toBe(undefined)
49+
src: {},
50+
},
8551
})
52+
const fn = createLibVersionValidator({
53+
cwd: '',
54+
physicalFilename: '/a/src/b.js',
55+
}, () => 'file:///a/node_modules/@nextcloud/vue/dist/index.js')
56+
expect(fn('8.22.0')).toBe(true)
57+
expect(fn('8.23.0')).toBe(true)
58+
expect(fn('8.23.1')).toBe(true)
59+
expect(fn('8.24.0')).toBe(false)
60+
expect(fn('9.0.0')).toBe(false)
8661
})
8762

88-
describe('createLibVersionValidator', () => {
89-
it('no config', () => {
90-
const fn = createLibVersionValidator({
91-
cwd: '',
92-
physicalFilename: '',
93-
})
94-
expect(fn('8.22.0')).toBe(false)
95-
expect(fn('8.23.0')).toBe(false)
96-
expect(fn('8.23.1')).toBe(false)
97-
expect(fn('8.24.0')).toBe(false)
98-
expect(fn('9.0.0')).toBe(false)
99-
})
100-
101-
describe('package.json', () => {
102-
afterEach(() => vol.reset())
103-
104-
it('returns false without nextcloud/vue in dependencies', () => {
105-
vol.fromNestedJSON({
106-
'/a': {
107-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{}}',
108-
src: { },
109-
},
110-
})
111-
const fn = createLibVersionValidator({
112-
cwd: '',
113-
physicalFilename: '/a/src/b.js',
114-
})
115-
expect(fn('8.22.0')).toBe(false)
116-
expect(fn('8.23.0')).toBe(false)
117-
expect(fn('8.23.1')).toBe(false)
118-
expect(fn('8.24.0')).toBe(false)
119-
expect(fn('9.0.0')).toBe(false)
120-
})
121-
122-
it('works with physical filename', () => {
123-
vol.fromNestedJSON({
124-
'/a': {
125-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{"@nextcloud/vue":"^8.23.1"}}',
126-
src: { },
127-
},
128-
})
129-
const fn = createLibVersionValidator({
130-
cwd: '',
131-
physicalFilename: '/a/src/b.js',
132-
})
133-
expect(fn('8.22.0')).toBe(true)
134-
expect(fn('8.23.0')).toBe(true)
135-
expect(fn('8.23.1')).toBe(true)
136-
expect(fn('8.24.0')).toBe(false)
137-
expect(fn('9.0.0')).toBe(false)
138-
})
139-
140-
it('works with cwd', () => {
141-
vol.fromNestedJSON({
142-
'/a': {
143-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{"@nextcloud/vue":"^8.23.1"}}',
144-
src: { },
145-
},
146-
})
147-
const fn = createLibVersionValidator({
148-
cwd: '/a',
149-
physicalFilename: 'src/b.js',
150-
})
151-
expect(fn('8.22.0')).toBe(true)
152-
expect(fn('8.23.0')).toBe(true)
153-
expect(fn('8.23.1')).toBe(true)
154-
expect(fn('8.24.0')).toBe(false)
155-
expect(fn('9.0.0')).toBe(false)
156-
})
157-
158-
it('works with several dependency sources', () => {
159-
vol.fromNestedJSON({
160-
'/a': {
161-
'package.json': '{"name": "my-app","version": "0.1.0","dependencies":{"@nextcloud/vue":"^8.24.0"},"devDependencies":{"@nextcloud/vue":"^8.24.0"},"peerDependencies":{"@nextcloud/vue":"^8.23.1"}}',
162-
src: { },
63+
it('works with cwd', () => {
64+
vol.fromNestedJSON({
65+
'/a': {
66+
node_modules: {
67+
'@nextcloud': {
68+
vue: {
69+
'package.json': '{"version": "8.23.1"}',
70+
},
16371
},
164-
})
165-
const fn = createLibVersionValidator({
166-
cwd: '/a',
167-
physicalFilename: 'src/b.js',
168-
})
169-
expect(fn('8.22.0')).toBe(true)
170-
expect(fn('8.23.0')).toBe(true)
171-
expect(fn('8.23.1')).toBe(true)
172-
expect(fn('8.24.0')).toBe(false)
173-
expect(fn('9.0.0')).toBe(false)
174-
})
72+
},
73+
src: {},
74+
},
17575
})
76+
const fn = createLibVersionValidator({
77+
cwd: '/a',
78+
physicalFilename: 'src/b.js',
79+
}, () => 'file:///a/node_modules/@nextcloud/vue/dist/index.js')
80+
81+
expect(fn('8.22.0')).toBe(true)
82+
expect(fn('8.23.0')).toBe(true)
83+
expect(fn('8.23.1')).toBe(true)
84+
expect(fn('8.24.0')).toBe(false)
85+
expect(fn('9.0.0')).toBe(false)
17686
})
17787
})

0 commit comments

Comments
 (0)