How to make Import recognize a new file extension?

New solution using RegisterFormat from Wolfram Function Repository:

ResourceFunction["RegisterFormat"]["BVH", <|"Extension" -> "bvh"|>]

Original Post:

Copy/paste from the excellent BVH package by @Sjoerd C. de Vries:

The code in this question (Registering/detecting an importer by file name extension) did not work. Neither did the answer. Wolfram support could not provide a more elegant solution so far. We use a trick here. In fact we don't have any options, but we need to add the option part to the argument template to be slightly more specific overall than an existing one that would also match. In this way we get to be evaluated before the other one, otherwise we'd be shadowed.

ImportExport`RegisterImport["BVH", BVHImporter`BVHImport];
BVHImporter`BVHImport[filename_String] := BVHImporter`BVHGet[Import[filename, "String"]];

Unprotect[Import];

Import[name_String, opts___?OptionQ] :=
    Import[name, "BVH", opts] /; FileExtension[name] === "bvh";

Protect[Import];

Related tutorial from the documentation: Developing an Import Converter


Here's a nice trick. We can simply figure out how FileFormat works and write a little wrapper, which frees us from overloading Import:

withFileFormat[formatName_ -> fileExtensions_, expr_] :=

  Internal`InheritedBlock[{
    FileFormatDump`$FILEFORMATS,
    FileFormatDump`$FILEFORMATMATRIX
    },
   FileFormatDump`$FILEFORMATS =
    Append[FileFormatDump`$FILEFORMATS, formatName];
   FileFormatDump`$FILEFORMATMATRIX[formatName] =
    {
     formatName,
     False, False, False, False, False,
     "*." <> # & /@ fileExtensions,
     {}, None, {}
     };
   expr
   ];
withFileFormat~SetAttributes~HoldRest

Then we check it on FileFormat:

withFileFormat["SHELL" -> {"sh"},
 FileFormat["~/Desktop/Bugs.sh"]
 ]

"SHELL"

And if I had a definition for "SHELL" this should work:

withFileFormat["SHELL" -> {"sh"},
 Import["~/Desktop/Bugs.sh"]
 ]

Import::infer: Cannot infer format of file Bugs.sh.

$Failed

On the plus side this differs from the default behavior in 11.3 which is to import as "Text".

Registering an alias for a new format will probably require more digging.

One noteworthy thing: if you drop the Internal`InheritedBlock this is literally just registering a new FileFormat.