Description
Let's start by making a file with multiple extensions: test.foo.bar
> fs.writeFileSync('test.foo.bar', 'NOT_JS', 'utf8')
undefined
So we already know that require.extensions
doesn't work with multiple extensions:
> require.extensions['.foo.bar'] = (module, path) => console.log('required .foo.bar', path)
[Function]
> require('./test.foo.bar')
ReferenceError: NOT_JS is not defined
[...stack trace elided...]
And that it will use the last component of the extension instead:
> require.extensions['.bar'] = (module, path) => console.log('required .bar', path)
[Function]
> require('./test.foo.bar')
required .bar /private/tmp/test.foo.bar
{}
Which is all perfectly obvious and by design. But did you know that the path searching uses the whole extension? Which means:
> delete require.extensions['.foo.bar'] // Since it's never called anyway
> require('./test')
Error: Cannot find module './test'
[...stack trace elided...]
However:
> require.extensions['.foo.bar'] = "SURPRISE!"
> require('./test')
required .bar /private/tmp/test.foo.bar
{}
In summary: to hook the requiring of a file with multiple extensions without specifying it, you must:
- Set
require.extensions
for the last component of the extension to your hook function - Set
require.extensions
for the entire extension to any value.
This is due to differences in how Module._findPath
and Module.load
deal with extensions: the former adds each extension in require.extensions
to the basename and tests it for existence (via tryExtensions
). The latter works the other way around; it removes everything up to the last part of the extension (via path.extname
) and then checks if it is present in require.extensions
.
I know it's policy not to fix bugs in module
that might help compile-to-js languages, but I figure someone else might come across the same problem and this report could be useful to them.