@@ -80,6 +80,14 @@ const META_FIELDS = new Set(["name"]);
8080 */
8181const FILES_AND_IGNORES_SCHEMA = new ObjectSchema ( filesAndIgnoresSchema ) ;
8282
83+ // Precomputed constant objects returned by `ConfigArray.getConfigWithStatus`.
84+
85+ const CONFIG_WITH_STATUS_EXTERNAL = Object . freeze ( { status : "external" } ) ;
86+ const CONFIG_WITH_STATUS_IGNORED = Object . freeze ( { status : "ignored" } ) ;
87+ const CONFIG_WITH_STATUS_UNCONFIGURED = Object . freeze ( {
88+ status : "unconfigured" ,
89+ } ) ;
90+
8391/**
8492 * Wrapper error for config validation errors that adds a name to the front of the
8593 * error message.
@@ -820,11 +828,14 @@ export class ConfigArray extends Array {
820828 }
821829
822830 /**
823- * Returns the config object for a given file path.
831+ * Returns the config object for a given file path and a status that can be used to determine why a file has no config .
824832 * @param {string } filePath The complete path of a file to get a config for.
825- * @returns {Object } The config object for this file.
833+ * @returns {{ config?: Object, status: "ignored"|"external"|"unconfigured"|"matched" } }
834+ * An object with an optional property `config` and property `status`.
835+ * `config` is the config object for the specified file as returned by {@linkcode ConfigArray.getConfig},
836+ * `status` a is one of the constants returned by {@linkcode ConfigArray.getConfigStatus}.
826837 */
827- getConfig ( filePath ) {
838+ getConfigWithStatus ( filePath ) {
828839 assertNormalized ( this ) ;
829840
830841 const cache = this [ ConfigArraySymbol . configCache ] ;
@@ -834,28 +845,35 @@ export class ConfigArray extends Array {
834845 return cache . get ( filePath ) ;
835846 }
836847
837- let finalConfig ;
848+ // check to see if the file is outside the base path
849+
850+ const relativeFilePath = path . relative ( this . basePath , filePath ) ;
851+
852+ if ( relativeFilePath . startsWith ( ".." ) ) {
853+ debug ( `No config for file ${ filePath } outside of base path` ) ;
854+
855+ // cache and return result
856+ cache . set ( filePath , CONFIG_WITH_STATUS_EXTERNAL ) ;
857+ return CONFIG_WITH_STATUS_EXTERNAL ;
858+ }
838859
839860 // next check to see if the file should be ignored
840861
841862 // check if this should be ignored due to its directory
842863 if ( this . isDirectoryIgnored ( path . dirname ( filePath ) ) ) {
843864 debug ( `Ignoring ${ filePath } based on directory pattern` ) ;
844865
845- // cache and return result - finalConfig is undefined at this point
846- cache . set ( filePath , finalConfig ) ;
847- return finalConfig ;
866+ // cache and return result
867+ cache . set ( filePath , CONFIG_WITH_STATUS_IGNORED ) ;
868+ return CONFIG_WITH_STATUS_IGNORED ;
848869 }
849870
850- // TODO: Maybe move elsewhere?
851- const relativeFilePath = path . relative ( this . basePath , filePath ) ;
852-
853871 if ( shouldIgnorePath ( this . ignores , filePath , relativeFilePath ) ) {
854872 debug ( `Ignoring ${ filePath } based on file pattern` ) ;
855873
856- // cache and return result - finalConfig is undefined at this point
857- cache . set ( filePath , finalConfig ) ;
858- return finalConfig ;
874+ // cache and return result
875+ cache . set ( filePath , CONFIG_WITH_STATUS_IGNORED ) ;
876+ return CONFIG_WITH_STATUS_IGNORED ;
859877 }
860878
861879 // filePath isn't automatically ignored, so try to construct config
@@ -949,24 +967,25 @@ export class ConfigArray extends Array {
949967 if ( ! matchFound ) {
950968 debug ( `No matching configs found for ${ filePath } ` ) ;
951969
952- // cache and return result - finalConfig is undefined at this point
953- cache . set ( filePath , finalConfig ) ;
954- return finalConfig ;
970+ // cache and return result
971+ cache . set ( filePath , CONFIG_WITH_STATUS_UNCONFIGURED ) ;
972+ return CONFIG_WITH_STATUS_UNCONFIGURED ;
955973 }
956974
957975 // check to see if there is a config cached by indices
958- finalConfig = cache . get ( matchingConfigIndices . toString ( ) ) ;
976+ const indicesKey = matchingConfigIndices . toString ( ) ;
977+ let configWithStatus = cache . get ( indicesKey ) ;
959978
960- if ( finalConfig ) {
979+ if ( configWithStatus ) {
961980 // also store for filename for faster lookup next time
962- cache . set ( filePath , finalConfig ) ;
981+ cache . set ( filePath , configWithStatus ) ;
963982
964- return finalConfig ;
983+ return configWithStatus ;
965984 }
966985
967986 // otherwise construct the config
968987
969- finalConfig = matchingConfigIndices . reduce ( ( result , index ) => {
988+ let finalConfig = matchingConfigIndices . reduce ( ( result , index ) => {
970989 try {
971990 return this [ ConfigArraySymbol . schema ] . merge (
972991 result ,
@@ -979,10 +998,36 @@ export class ConfigArray extends Array {
979998
980999 finalConfig = this [ ConfigArraySymbol . finalizeConfig ] ( finalConfig ) ;
9811000
982- cache . set ( filePath , finalConfig ) ;
983- cache . set ( matchingConfigIndices . toString ( ) , finalConfig ) ;
1001+ configWithStatus = Object . freeze ( {
1002+ config : finalConfig ,
1003+ status : "matched" ,
1004+ } ) ;
1005+ cache . set ( filePath , configWithStatus ) ;
1006+ cache . set ( indicesKey , configWithStatus ) ;
1007+
1008+ return configWithStatus ;
1009+ }
9841010
985- return finalConfig ;
1011+ /**
1012+ * Returns the config object for a given file path.
1013+ * @param {string } filePath The complete path of a file to get a config for.
1014+ * @returns {Object|undefined } The config object for this file or `undefined`.
1015+ */
1016+ getConfig ( filePath ) {
1017+ return this . getConfigWithStatus ( filePath ) . config ;
1018+ }
1019+
1020+ /**
1021+ * Determines whether a file has a config or why it doesn't.
1022+ * @param {string } filePath The complete path of the file to check.
1023+ * @returns {"ignored"|"external"|"unconfigured"|"matched" } One of the following values:
1024+ * * `"ignored"`: the file is ignored
1025+ * * `"external"`: the file is outside the base path
1026+ * * `"unconfigured"`: the file is not matched by any config
1027+ * * `"matched"`: the file has a matching config
1028+ */
1029+ getConfigStatus ( filePath ) {
1030+ return this . getConfigWithStatus ( filePath ) . status ;
9861031 }
9871032
9881033 /**
@@ -1001,7 +1046,7 @@ export class ConfigArray extends Array {
10011046 * @returns {boolean } True if the path is ignored, false if not.
10021047 */
10031048 isFileIgnored ( filePath ) {
1004- return this . getConfig ( filePath ) === undefined ;
1049+ return this . getConfigStatus ( filePath ) === "ignored" ;
10051050 }
10061051
10071052 /**
0 commit comments