Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/eslint-plugin-import
Path: blob/main/tests/src/core/resolve.js
829 views
1
import { expect } from 'chai';
2
import eslintPkg from 'eslint/package.json';
3
import semver from 'semver';
4
5
import resolve, { CASE_SENSITIVE_FS, fileExistsWithCaseSync } from 'eslint-module-utils/resolve';
6
7
import * as path from 'path';
8
import * as fs from 'fs';
9
import * as utils from '../utils';
10
11
describe('resolve', function () {
12
// We don't want to test for a specific stack, just that it was there in the error message.
13
function replaceErrorStackForTest(str) {
14
return typeof str === 'string' ? str.replace(/(\n\s+at .+:\d+\)?)+$/, '\n<stack-was-here>') : str;
15
}
16
17
it('throws on bad parameters', function () {
18
expect(resolve.bind(null, null, null)).to.throw(Error);
19
});
20
21
it('resolves via a custom resolver with interface version 1', function () {
22
const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v1' });
23
24
expect(resolve( '../files/foo'
25
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
26
)).to.equal(utils.testFilePath('./bar.jsx'));
27
28
expect(resolve( '../files/exception'
29
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }),
30
)).to.equal(undefined);
31
32
expect(resolve( '../files/not-found'
33
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }),
34
)).to.equal(undefined);
35
});
36
37
it('resolves via a custom resolver with interface version 1 assumed if not specified', function () {
38
const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-no-version' });
39
40
expect(resolve( '../files/foo'
41
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
42
)).to.equal(utils.testFilePath('./bar.jsx'));
43
44
expect(resolve( '../files/exception'
45
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }),
46
)).to.equal(undefined);
47
48
expect(resolve( '../files/not-found'
49
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }),
50
)).to.equal(undefined);
51
});
52
53
it('resolves via a custom resolver with interface version 2', function () {
54
const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v2' });
55
const testContextReports = [];
56
testContext.report = function (reportInfo) {
57
testContextReports.push(reportInfo);
58
};
59
60
expect(resolve( '../files/foo'
61
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
62
)).to.equal(utils.testFilePath('./bar.jsx'));
63
64
testContextReports.length = 0;
65
expect(resolve( '../files/exception'
66
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }),
67
)).to.equal(undefined);
68
expect(testContextReports[0]).to.be.an('object');
69
expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: foo-bar-resolver-v2 resolve test exception\n<stack-was-here>');
70
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
71
72
testContextReports.length = 0;
73
expect(resolve( '../files/not-found'
74
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }),
75
)).to.equal(undefined);
76
expect(testContextReports.length).to.equal(0);
77
});
78
79
it('respects import/resolver as array of strings', function () {
80
const testContext = utils.testContext({ 'import/resolver': [ './foo-bar-resolver-v2', './foo-bar-resolver-v1' ] });
81
82
expect(resolve( '../files/foo'
83
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
84
)).to.equal(utils.testFilePath('./bar.jsx'));
85
});
86
87
it('respects import/resolver as object', function () {
88
const testContext = utils.testContext({ 'import/resolver': { './foo-bar-resolver-v2': {} } });
89
90
expect(resolve( '../files/foo'
91
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
92
)).to.equal(utils.testFilePath('./bar.jsx'));
93
});
94
95
it('respects import/resolver as array of objects', function () {
96
const testContext = utils.testContext({ 'import/resolver': [ { './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} } ] });
97
98
expect(resolve( '../files/foo'
99
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
100
)).to.equal(utils.testFilePath('./bar.jsx'));
101
});
102
103
it('finds resolvers from the source files rather than eslint-module-utils', function () {
104
const testContext = utils.testContext({ 'import/resolver': { 'foo': {} } });
105
106
expect(resolve( '../files/foo'
107
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
108
)).to.equal(utils.testFilePath('./bar.jsx'));
109
});
110
111
it('reports invalid import/resolver config', function () {
112
const testContext = utils.testContext({ 'import/resolver': 123.456 });
113
const testContextReports = [];
114
testContext.report = function (reportInfo) {
115
testContextReports.push(reportInfo);
116
};
117
118
testContextReports.length = 0;
119
expect(resolve( '../files/foo'
120
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
121
)).to.equal(undefined);
122
expect(testContextReports[0]).to.be.an('object');
123
expect(testContextReports[0].message).to.equal('Resolve error: invalid resolver config');
124
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
125
});
126
127
it('reports loaded resolver with invalid interface', function () {
128
const resolverName = './foo-bar-resolver-invalid';
129
const testContext = utils.testContext({ 'import/resolver': resolverName });
130
const testContextReports = [];
131
testContext.report = function (reportInfo) {
132
testContextReports.push(reportInfo);
133
};
134
testContextReports.length = 0;
135
expect(resolve( '../files/foo'
136
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }),
137
)).to.equal(undefined);
138
expect(testContextReports[0]).to.be.an('object');
139
expect(testContextReports[0].message).to.equal(`Resolve error: ${resolverName} with invalid interface loaded as resolver`);
140
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
141
});
142
143
it('respects import/resolve extensions', function () {
144
const testContext = utils.testContext({ 'import/resolve': { 'extensions': ['.jsx'] } });
145
146
expect(resolve( './jsx/MyCoolComponent'
147
, testContext,
148
)).to.equal(utils.testFilePath('./jsx/MyCoolComponent.jsx'));
149
});
150
151
it('reports load exception in a user resolver', function () {
152
const testContext = utils.testContext({ 'import/resolver': './load-error-resolver' });
153
const testContextReports = [];
154
testContext.report = function (reportInfo) {
155
testContextReports.push(reportInfo);
156
};
157
158
expect(resolve( '../files/exception'
159
, Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }),
160
)).to.equal(undefined);
161
expect(testContextReports[0]).to.be.an('object');
162
expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: SyntaxError: TEST SYNTAX ERROR\n<stack-was-here>');
163
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
164
});
165
166
// context.getPhysicalFilename() is available in ESLint 7.28+
167
(semver.satisfies(eslintPkg.version, '>= 7.28') ? describe : describe.skip)('getPhysicalFilename()', () => {
168
function unexpectedCallToGetFilename() {
169
throw new Error('Expected to call to getPhysicalFilename() instead of getFilename()');
170
}
171
172
it('resolves via a custom resolver with interface version 1', function () {
173
const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v1' });
174
175
expect(resolve( '../files/foo'
176
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
177
)).to.equal(utils.testFilePath('./bar.jsx'));
178
179
expect(resolve( '../files/exception'
180
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }),
181
)).to.equal(undefined);
182
183
expect(resolve( '../files/not-found'
184
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }),
185
)).to.equal(undefined);
186
});
187
188
it('resolves via a custom resolver with interface version 1 assumed if not specified', function () {
189
const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-no-version' });
190
191
expect(resolve( '../files/foo'
192
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
193
)).to.equal(utils.testFilePath('./bar.jsx'));
194
195
expect(resolve( '../files/exception'
196
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }),
197
)).to.equal(undefined);
198
199
expect(resolve( '../files/not-found'
200
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }),
201
)).to.equal(undefined);
202
});
203
204
it('resolves via a custom resolver with interface version 2', function () {
205
const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v2' });
206
const testContextReports = [];
207
testContext.report = function (reportInfo) {
208
testContextReports.push(reportInfo);
209
};
210
211
expect(resolve( '../files/foo'
212
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
213
)).to.equal(utils.testFilePath('./bar.jsx'));
214
215
testContextReports.length = 0;
216
expect(resolve( '../files/exception'
217
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }),
218
)).to.equal(undefined);
219
expect(testContextReports[0]).to.be.an('object');
220
expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: foo-bar-resolver-v2 resolve test exception\n<stack-was-here>');
221
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
222
223
testContextReports.length = 0;
224
expect(resolve( '../files/not-found'
225
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }),
226
)).to.equal(undefined);
227
expect(testContextReports.length).to.equal(0);
228
});
229
230
it('respects import/resolver as array of strings', function () {
231
const testContext = utils.testContext({ 'import/resolver': [ './foo-bar-resolver-v2', './foo-bar-resolver-v1' ] });
232
233
expect(resolve( '../files/foo'
234
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
235
)).to.equal(utils.testFilePath('./bar.jsx'));
236
});
237
238
it('respects import/resolver as object', function () {
239
const testContext = utils.testContext({ 'import/resolver': { './foo-bar-resolver-v2': {} } });
240
241
expect(resolve( '../files/foo'
242
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
243
)).to.equal(utils.testFilePath('./bar.jsx'));
244
});
245
246
it('respects import/resolver as array of objects', function () {
247
const testContext = utils.testContext({ 'import/resolver': [ { './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} } ] });
248
249
expect(resolve( '../files/foo'
250
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
251
)).to.equal(utils.testFilePath('./bar.jsx'));
252
});
253
254
it('finds resolvers from the source files rather than eslint-module-utils', function () {
255
const testContext = utils.testContext({ 'import/resolver': { 'foo': {} } });
256
257
expect(resolve( '../files/foo'
258
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
259
)).to.equal(utils.testFilePath('./bar.jsx'));
260
});
261
262
it('reports invalid import/resolver config', function () {
263
const testContext = utils.testContext({ 'import/resolver': 123.456 });
264
const testContextReports = [];
265
testContext.report = function (reportInfo) {
266
testContextReports.push(reportInfo);
267
};
268
269
testContextReports.length = 0;
270
expect(resolve( '../files/foo'
271
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
272
)).to.equal(undefined);
273
expect(testContextReports[0]).to.be.an('object');
274
expect(testContextReports[0].message).to.equal('Resolve error: invalid resolver config');
275
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
276
});
277
278
it('reports loaded resolver with invalid interface', function () {
279
const resolverName = './foo-bar-resolver-invalid';
280
const testContext = utils.testContext({ 'import/resolver': resolverName });
281
const testContextReports = [];
282
testContext.report = function (reportInfo) {
283
testContextReports.push(reportInfo);
284
};
285
testContextReports.length = 0;
286
expect(resolve( '../files/foo'
287
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }),
288
)).to.equal(undefined);
289
expect(testContextReports[0]).to.be.an('object');
290
expect(testContextReports[0].message).to.equal(`Resolve error: ${resolverName} with invalid interface loaded as resolver`);
291
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
292
});
293
294
it('respects import/resolve extensions', function () {
295
const testContext = utils.testContext({ 'import/resolve': { 'extensions': ['.jsx'] } });
296
297
expect(resolve( './jsx/MyCoolComponent'
298
, testContext,
299
)).to.equal(utils.testFilePath('./jsx/MyCoolComponent.jsx'));
300
});
301
302
it('reports load exception in a user resolver', function () {
303
const testContext = utils.testContext({ 'import/resolver': './load-error-resolver' });
304
const testContextReports = [];
305
testContext.report = function (reportInfo) {
306
testContextReports.push(reportInfo);
307
};
308
309
expect(resolve( '../files/exception'
310
, Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }),
311
)).to.equal(undefined);
312
expect(testContextReports[0]).to.be.an('object');
313
expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: SyntaxError: TEST SYNTAX ERROR\n<stack-was-here>');
314
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
315
});
316
});
317
318
const caseDescribe = (!CASE_SENSITIVE_FS ? describe : describe.skip);
319
caseDescribe('case sensitivity', function () {
320
let file;
321
const testContext = utils.testContext({
322
'import/resolve': { 'extensions': ['.jsx'] },
323
'import/cache': { lifetime: 0 },
324
});
325
const testSettings = testContext.settings;
326
before('resolve', function () {
327
file = resolve(
328
// Note the case difference 'MyUncoolComponent' vs 'MyUnCoolComponent'
329
'./jsx/MyUncoolComponent', testContext);
330
});
331
it('resolves regardless of case', function () {
332
expect(file, 'path to ./jsx/MyUncoolComponent').to.exist;
333
});
334
it('detects case does not match FS', function () {
335
expect(fileExistsWithCaseSync(file, testSettings))
336
.to.be.false;
337
});
338
it('detecting case does not include parent folder path (issue #720)', function () {
339
const f = path.join(process.cwd().toUpperCase(), './tests/files/jsx/MyUnCoolComponent.jsx');
340
expect(fileExistsWithCaseSync(f, testSettings))
341
.to.be.true;
342
});
343
it('detecting case should include parent folder path', function () {
344
const f = path.join(process.cwd().toUpperCase(), './tests/files/jsx/MyUnCoolComponent.jsx');
345
expect(fileExistsWithCaseSync(f, testSettings, true))
346
.to.be.false;
347
});
348
});
349
350
describe('rename cache correctness', function () {
351
const context = utils.testContext({
352
'import/cache': { 'lifetime': 1 },
353
});
354
355
const infiniteContexts = [ '∞', 'Infinity' ].map(inf => [inf,
356
utils.testContext({
357
'import/cache': { 'lifetime': inf },
358
})]);
359
360
361
const pairs = [
362
['./CaseyKasem.js', './CASEYKASEM2.js'],
363
];
364
365
pairs.forEach(([original, changed]) => {
366
describe(`${original} => ${changed}`, function () {
367
368
before('sanity check', function () {
369
expect(resolve(original, context)).to.exist;
370
expect(resolve(changed, context)).not.to.exist;
371
});
372
373
// settings are part of cache key
374
before('warm up infinite entries', function () {
375
infiniteContexts.forEach(([,c]) => {
376
expect(resolve(original, c)).to.exist;
377
});
378
});
379
380
before('rename', function (done) {
381
fs.rename(
382
utils.testFilePath(original),
383
utils.testFilePath(changed),
384
done);
385
});
386
387
before('verify rename', (done) =>
388
fs.exists(
389
utils.testFilePath(changed),
390
exists => done(exists ? null : new Error('new file does not exist'))));
391
392
it('gets cached values within cache lifetime', function () {
393
// get cached values initially
394
expect(resolve(original, context)).to.exist;
395
});
396
397
it('gets updated values immediately', function () {
398
// get cached values initially
399
expect(resolve(changed, context)).to.exist;
400
});
401
402
// special behavior for infinity
403
describe('infinite cache', function () {
404
this.timeout(1500);
405
406
before((done) => setTimeout(done, 1100));
407
408
infiniteContexts.forEach(([inf, infiniteContext]) => {
409
it(`lifetime: ${inf} still gets cached values after ~1s`, function () {
410
expect(resolve(original, infiniteContext), original).to.exist;
411
});
412
});
413
414
});
415
416
describe('finite cache', function () {
417
this.timeout(1200);
418
before((done) => setTimeout(done, 1000));
419
it('gets correct values after cache lifetime', function () {
420
expect(resolve(original, context)).not.to.exist;
421
expect(resolve(changed, context)).to.exist;
422
});
423
});
424
425
after('restore original case', function (done) {
426
fs.rename(
427
utils.testFilePath(changed),
428
utils.testFilePath(original),
429
done);
430
});
431
});
432
});
433
});
434
435
});
436
437