25
25
var vm = require ( 'vm' ) ;
26
26
var dirname = require ( 'path' ) . dirname ;
27
27
var resolve = require ( 'path' ) . resolve ;
28
+ var join = require ( 'path' ) . join ;
28
29
var existsSync = require ( 'fs' ) . existsSync ; // eslint-disable-line node/no-sync
30
+ var readFileSync = require ( 'fs' ) . readFileSync ; // eslint-disable-line node/no-sync
29
31
var logger = require ( 'debug' ) ;
30
32
var isNull = require ( '@stdlib/assert/is-null' ) ;
31
33
var isNumber = require ( '@stdlib/assert/is-number' ) ;
32
34
var contains = require ( '@stdlib/assert/contains' ) ;
33
35
var replace = require ( '@stdlib/string/replace' ) ;
36
+ var endsWith = require ( '@stdlib/string/ends-with' ) ;
37
+ var trim = require ( '@stdlib/string/trim' ) ;
34
38
var objectKeys = require ( '@stdlib/utils/keys' ) ;
35
39
var compareValues = require ( '@stdlib/_tools/doctest/compare-values' ) ;
36
40
var createAnnotationValue = require ( '@stdlib/_tools/doctest/create-annotation-value' ) ;
@@ -63,7 +67,7 @@ var rule;
63
67
* @returns {string } escaped string
64
68
*/
65
69
function escapeRegex ( str ) {
66
- return str . replace ( RE_SPECIAL_CHARS , '\\$&' ) ;
70
+ return replace ( str , RE_SPECIAL_CHARS , '\\$&' ) ;
67
71
}
68
72
69
73
/**
@@ -133,22 +137,73 @@ function findName( scope, expected ) {
133
137
}
134
138
135
139
/**
136
- * Resolves the implementation path relative to a TypeScript declaration file.
140
+ * Finds the nearest package.json file by traversing up the directory tree.
141
+ *
142
+ * @private
143
+ * @param {string } filepath - starting file path
144
+ * @returns {string|null } path to package.json or null if not found
145
+ */
146
+ function findNearestPackageJSON ( filepath ) {
147
+ var pkgPath ;
148
+ var dir ;
149
+
150
+ dir = dirname ( resolve ( filepath ) ) ;
151
+ while ( dir !== dirname ( dir ) ) { // Stop at root
152
+ pkgPath = join ( dir , 'package.json' ) ;
153
+ if ( existsSync ( pkgPath ) ) {
154
+ return pkgPath ;
155
+ }
156
+ dir = dirname ( dir ) ;
157
+ }
158
+ return null ;
159
+ }
160
+
161
+ /**
162
+ * Resolves the implementation path from package.json.
137
163
*
138
164
* @private
139
165
* @param {string } filepath - TypeScript declaration file path
140
- * @param {string } implementationPath - relative path to implementation
141
166
* @returns {string|null } resolved implementation path or null if not found
142
167
*/
143
- function resolveImplementationPath ( filepath , implementationPath ) {
168
+ function resolveImplementationPath ( filepath ) {
144
169
var implPath ;
145
- var baseDir ;
170
+ var mainPath ;
171
+ var pkgJSON ;
172
+ var pkgPath ;
173
+ var pkgDir ;
174
+
175
+ // Find the nearest package.json:
176
+ pkgPath = findNearestPackageJSON ( filepath ) ;
177
+ if ( ! pkgPath ) {
178
+ debug ( 'Could not find package.json for: ' + filepath ) ;
179
+ return null ;
180
+ }
181
+
182
+ // Read and parse package.json:
183
+ try {
184
+ pkgJSON = JSON . parse ( readFileSync ( pkgPath , 'utf8' ) ) ;
185
+ } catch ( err ) {
186
+ debug ( 'Could not read/parse package.json: ' + pkgPath + '. Error: ' + err . message ) ;
187
+ return null ;
188
+ }
189
+
190
+ // Get the main entry point:
191
+ mainPath = pkgJSON . main || './lib' ;
192
+ pkgDir = dirname ( pkgPath ) ;
193
+ implPath = resolve ( pkgDir , mainPath ) ;
146
194
147
- baseDir = dirname ( resolve ( filepath ) ) ;
148
- implPath = resolve ( baseDir , implementationPath ) ;
195
+ // Check if it's a directory or file:
149
196
if ( existsSync ( implPath ) ) {
150
197
return implPath ;
151
198
}
199
+ // Try with index.js if it's a directory:
200
+ if ( existsSync ( join ( implPath , 'index.js' ) ) ) {
201
+ return join ( implPath , 'index.js' ) ;
202
+ }
203
+ // Try adding .js extension:
204
+ if ( existsSync ( implPath + '.js' ) ) {
205
+ return implPath + '.js' ;
206
+ }
152
207
return null ;
153
208
}
154
209
@@ -161,7 +216,7 @@ function resolveImplementationPath( filepath, implementationPath ) {
161
216
*/
162
217
function cleanTSDocComment ( comment ) {
163
218
// Remove opening /** and closing */
164
- var cleaned = comment . replace ( / ^ \/ \* \* / , '' ) . replace ( / \* \/ $ / , '' ) ;
219
+ var cleaned = replace ( replace ( comment , / ^ \/ \* \* / , '' ) , / \* \/ $ / , '' ) ;
165
220
166
221
// Remove * at the beginning of lines
167
222
cleaned = replace ( cleaned , RE_COMMENT_PREFIX , '' ) ;
@@ -329,15 +384,13 @@ function processExampleCode( code, commentIdx, comments, scope, report, opts, so
329
384
function main ( context ) {
330
385
var sourceCode ;
331
386
var filename ;
332
- var options ;
333
387
var dir ;
334
388
335
389
sourceCode = context . getSourceCode ( ) ;
336
390
filename = context . getFilename ( ) ;
337
- options = context . options [ 0 ] || { } ;
338
391
339
392
// Only process TypeScript declaration files:
340
- if ( ! filename . endsWith ( '.d.ts' ) ) {
393
+ if ( ! endsWith ( filename , '.d.ts' ) ) {
341
394
return { } ;
342
395
}
343
396
@@ -379,7 +432,6 @@ function main( context ) {
379
432
* @private
380
433
*/
381
434
function validate ( ) {
382
- var implementationPath ;
383
435
var implPath ;
384
436
var examples ;
385
437
var comments ;
@@ -397,11 +449,10 @@ function main( context ) {
397
449
regexCache = { } ;
398
450
lineCountCache = { } ;
399
451
400
- // Resolve implementation path relative to TypeScript declaration file path:
401
- implementationPath = options . implementationPath || '../../lib' ;
402
- implPath = resolveImplementationPath ( filename , implementationPath ) ;
452
+ // Resolve implementation path from nearest package.json:
453
+ implPath = resolveImplementationPath ( filename ) ;
403
454
if ( ! implPath ) {
404
- debug ( 'Could not resolve implementation path: ' + implementationPath + ' from ' + filename ) ;
455
+ debug ( 'Could not resolve implementation path from package.json for: ' + filename ) ;
405
456
return ;
406
457
}
407
458
@@ -442,7 +493,7 @@ function main( context ) {
442
493
}
443
494
444
495
for ( j = 0 ; j < examples . length ; j ++ ) {
445
- code = examples [ j ] . trim ( ) ;
496
+ code = trim ( examples [ j ] ) ;
446
497
447
498
// Process the example code and validate annotations (reusing VM context):
448
499
processExampleCode ( code , i , comments , scope , report , opts , sourceCode ) ;
@@ -465,18 +516,7 @@ rule = {
465
516
'description' : 'ensure return annotations in TSDoc examples match the actual output'
466
517
} ,
467
518
'fixable' : 'code' ,
468
- 'schema' : [
469
- {
470
- 'type' : 'object' ,
471
- 'properties' : {
472
- 'implementationPath' : {
473
- 'type' : 'string' ,
474
- 'default' : '../../lib'
475
- }
476
- } ,
477
- 'additionalProperties' : false
478
- }
479
- ]
519
+ 'schema' : [ ]
480
520
} ,
481
521
'create' : main
482
522
} ;
0 commit comments