@@ -8,6 +8,8 @@ var debug = require('debug')('loopback:boot:compiler');
88var Module = require ( 'module' ) ;
99var _ = require ( 'lodash' ) ;
1010
11+ var FILE_EXTENSION_JSON = '.json' ;
12+
1113/**
1214 * Gather all bootstrap-related configuration data and compile it into
1315 * a single object containing instruction for `boot.execute`.
@@ -160,12 +162,11 @@ function findScripts(dir) {
160162 }
161163
162164 var filepath = path . resolve ( path . join ( dir , filename ) ) ;
163- var ext = path . extname ( filename ) ;
164165 var stats = fs . statSync ( filepath ) ;
165166
166167 // only require files supported by require.extensions (.txt .md etc.)
167168 if ( stats . isFile ( ) ) {
168- if ( ext in require . extensions )
169+ if ( isPreferredExtension ( filename ) )
169170 results . push ( filepath ) ;
170171 else
171172 debug ( 'Skipping file %s - unknown extension' , filepath ) ;
@@ -299,8 +300,8 @@ function findModelDefinitions(rootDir, sources) {
299300 return registry ;
300301}
301302
302- function resolveAppPath ( rootDir , relativePath ) {
303- var resolvedPath = tryResolveAppPath ( rootDir , relativePath ) ;
303+ function resolveAppPath ( rootDir , relativePath , resolveOptions ) {
304+ var resolvedPath = tryResolveAppPath ( rootDir , relativePath , resolveOptions ) ;
304305 if ( resolvedPath === undefined ) {
305306 var err = new Error ( 'Cannot resolve path "' + relativePath + '"' ) ;
306307 err . code = 'PATH_NOT_FOUND' ;
@@ -309,12 +310,27 @@ function resolveAppPath(rootDir, relativePath) {
309310 return resolvedPath ;
310311}
311312
312- function tryResolveAppPath ( rootDir , relativePath ) {
313- var fullPath = path . resolve ( rootDir , relativePath ) ;
314- if ( fs . existsSync ( fullPath ) )
313+ function tryResolveAppPath ( rootDir , relativePath , resolveOptions ) {
314+ var fullPath ;
315+ var start = relativePath . substring ( 0 , 2 ) ;
316+
317+ /* In order to retain backward compatibility, while resolving
318+ * component path, `resolveOptions` parameter is added where
319+ * `resolveOptions.strict` = true,
320+ * means retain backward compatibility when resolving the path and
321+ * `resolveOptions.strict` = false,
322+ * does not enforce any such restriction when resolving the path */
323+ resolveOptions = resolveOptions || { strict : false } ;
324+
325+ if ( relativePath [ 0 ] === '/' ) {
326+ fullPath = relativePath ;
327+ } else if ( start === './' || start === '..' || ! resolveOptions . strict ) {
328+ fullPath = path . resolve ( rootDir , relativePath ) ;
329+ }
330+
331+ if ( fullPath && fs . existsSync ( fullPath ) )
315332 return fullPath ;
316333
317- var start = relativePath . substring ( 0 , 2 ) ;
318334 if ( start !== './' && start !== '..' ) {
319335 // Handle module-relative path, e.g. `loopback/common/models`
320336
@@ -339,6 +355,7 @@ function tryResolveAppPath(rootDir, relativePath) {
339355 return fs . existsSync ( candidate ) ;
340356 } )
341357 [ 0 ] ;
358+
342359 if ( fullPath )
343360 return fullPath ;
344361 } else {
@@ -356,27 +373,12 @@ function tryResolveAppPath(rootDir, relativePath) {
356373
357374function loadModelDefinition ( rootDir , jsonFile , allFiles ) {
358375 var definition = require ( jsonFile ) ;
359- var basename = path . basename ( jsonFile , path . extname ( jsonFile ) ) ;
360376
361377 // find a matching file with a supported extension like `.js` or `.coffee`
362- var base ;
363- var ext ;
364- var validFileType ;
365- var sourceFile = allFiles
366- . filter ( function ( f ) {
367- ext = path . extname ( f ) ;
368- base = path . basename ( f , ext ) ;
369- validFileType = ( ext !== '.node' ) && ( ext !== '.json' ) &&
370- ( ( typeof require . extensions [ ext ] ) === 'function' ) ;
371- return validFileType && ( base === basename ) ;
372- } ) [ 0 ] ;
378+ var sourceFile = fixFileExtension ( jsonFile , allFiles , true ) ;
373379
374- try {
375- sourceFile = path . join ( path . dirname ( jsonFile ) , sourceFile ) ;
376- sourceFile = require . resolve ( sourceFile ) ;
377- } catch ( err ) {
378- debug ( 'Model source code not found: %s - %s' , sourceFile , err . code || err ) ;
379- sourceFile = undefined ;
380+ if ( sourceFile === undefined ) {
381+ debug ( 'Model source code not found: %s' , sourceFile ) ;
380382 }
381383
382384 debug ( 'Found model "%s" - %s %s' , definition . name ,
@@ -456,7 +458,7 @@ function resolveMiddlewarePath(rootDir, middleware) {
456458 }
457459
458460 if ( ! fragment ) {
459- resolved . sourceFile = resolveAppPath ( rootDir , middlewarePath ) ;
461+ resolved . sourceFile = resolveAppScriptPath ( rootDir , middlewarePath ) ;
460462 return resolved ;
461463 }
462464
@@ -466,7 +468,7 @@ function resolveMiddlewarePath(rootDir, middleware) {
466468 // function
467469 var m = require ( pathName ) ;
468470 if ( typeof m [ fragment ] === 'function' ) {
469- resolved . sourceFile = resolveAppPath ( rootDir , middlewarePath ) ;
471+ resolved . sourceFile = resolveAppScriptPath ( rootDir , middlewarePath ) ;
470472 return resolved ;
471473 }
472474
@@ -484,7 +486,7 @@ function resolveMiddlewarePath(rootDir, middleware) {
484486
485487 for ( var ix in candidates ) {
486488 try {
487- resolved . sourceFile = resolveAppPath ( rootDir , candidates [ ix ] ) ;
489+ resolved . sourceFile = resolveAppScriptPath ( rootDir , candidates [ ix ] ) ;
488490 delete resolved . fragment ;
489491 return resolved ;
490492 }
@@ -513,16 +515,8 @@ function buildComponentInstructions(rootDir, componentConfig) {
513515 return Object . keys ( componentConfig )
514516 . filter ( function ( name ) { return ! ! componentConfig [ name ] ; } )
515517 . map ( function ( name ) {
516- var sourceFile ;
517- if ( name . indexOf ( './' ) === 0 || name . indexOf ( '../' ) === 0 ) {
518- // Relative path
519- sourceFile = path . resolve ( rootDir , name ) ;
520- } else {
521- sourceFile = require . resolve ( name ) ;
522- }
523-
524518 return {
525- sourceFile : sourceFile ,
519+ sourceFile : resolveAppScriptPath ( rootDir , name , { 'strict' : true } ) ,
526520 config : componentConfig [ name ]
527521 } ;
528522 } ) ;
@@ -538,3 +532,56 @@ function resolveRelativePaths(relativePaths, appRootDir) {
538532 }
539533 } ) ;
540534}
535+
536+ function getExcludedExtensions ( ) {
537+ return {
538+ '.json' : '.json' ,
539+ '.node' : 'node'
540+ } ;
541+ }
542+
543+ function isPreferredExtension ( filename ) {
544+ var includeExtensions = require . extensions ;
545+
546+ var ext = path . extname ( filename ) ;
547+ return ( ext in includeExtensions ) && ! ( ext in getExcludedExtensions ( ) ) ;
548+ }
549+
550+ function fixFileExtension ( filepath , files , onlyScriptsExportingFunction ) {
551+ var results = [ ] ;
552+ var otherFile ;
553+
554+ /* Prefer coffee scripts over json */
555+ if ( isPreferredExtension ( filepath ) ) return filepath ;
556+
557+ var basename = path . basename ( filepath , FILE_EXTENSION_JSON ) ;
558+ var sourceDir = path . dirname ( filepath ) ;
559+
560+ files . forEach ( function ( f ) {
561+ otherFile = path . resolve ( sourceDir , f ) ;
562+
563+ var stats = fs . statSync ( otherFile ) ;
564+ if ( stats . isFile ( ) ) {
565+ var otherFileExtension = path . extname ( f ) ;
566+
567+ if ( ! ( otherFileExtension in getExcludedExtensions ( ) ) &&
568+ path . basename ( f , otherFileExtension ) == basename ) {
569+ if ( ! onlyScriptsExportingFunction )
570+ results . push ( otherFile ) ;
571+ else if ( onlyScriptsExportingFunction &&
572+ ( typeof require . extensions [ otherFileExtension ] ) === 'function' ) {
573+ results . push ( otherFile ) ;
574+ }
575+ }
576+ }
577+ } ) ;
578+ return ( results . length > 0 ? results [ 0 ] : undefined ) ;
579+ }
580+
581+ function resolveAppScriptPath ( rootDir , relativePath , resolveOptions ) {
582+ var resolvedPath = resolveAppPath ( rootDir , relativePath , resolveOptions ) ;
583+ var sourceDir = path . dirname ( resolvedPath ) ;
584+ var files = tryReadDir ( sourceDir ) ;
585+ var fixedFile = fixFileExtension ( resolvedPath , files , false ) ;
586+ return ( fixedFile === undefined ? resolvedPath : fixedFile ) ;
587+ }
0 commit comments