28

I understand that we can use import {x} from "./file" and a variable x will be imported from file.js in the same directory. How is this handled if there are files of the same name with different extensions in the directory?

For example, if there were file.js and file.ts in the same directory, how would import {x} from "./file" behave? Would it depend on the version of JavaScript?

Paolo
  • 20,112
  • 21
  • 72
  • 113
curiousgeorge
  • 593
  • 1
  • 5
  • 14
  • [_"Certain bundlers may permit or require the use of the extension; check your environment"_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Syntax). You could probably have a look through the Webpack / Rollup / whatever source code – Phil Mar 20 '19 at 00:43

2 Answers2

42

Would it depend on the version of javascript?

No, it depends on the behavior of JavaScript runtime, that is, the thing that executes your script.

In a browser with support for ES Modules (ESM), no extensions will be added to the URL that you import - if your file for example has .js extension, you have to write import {x} from "./file.js". Browsers have no useful way of looking up which files with which extensions are available on the server.

In browsers without native support for ESM, you have to transpile your modules to a bundled format which can run in browser. In this case, it depends on the behaviour of the specific bundler you choose to use (see below).

In Node.js versions which support ESM, the runtime will not search for extensions, but it will resolve modules from node_modules by name. For example import 'lodash' could resolve to ./node_modules/lodash/index.mjs, without you needing to know that the extension of index.mjs.

In Node.js versions which do not support ESM, you can't use import - you have to transpile the module to CommonJS format first, which will end up using require. require has a list of extensions that it will search the filesystem for.

For example, if there were file.js and file.ts in the same directory, how would import {x} from "./file" behave?

It depends.

When you transpile or compile your script, which extensions are recognized depends on the compiler and the settings you provide for compilation.

In webpack, for example, there is predefined list of supported extensions - '.wasm', '.mjs', '.js', '.json', but it could be changed by using resolve.extension setting in your webpack.config.js file.

If you use webpack with ts-loader plugin, .ts file extension is also recognized, but the loader will try to make it so that .ts file is compiled into .js file, and will try to use that compiled .js file when bundling.

If you use plain typescript compiler to compile your scripts, the compiler will look for a file with '.ts' extension to perform type checking, but it will generate code which will look for a file with '.js' extension when you will run the script. Also, if the file with '.ts' extension is compiled, the compiler will write generated code in the file with '.js' extension and may overwrite your javascript file if you have one, depending on the setting which tells it where to output '.js' files.

snek
  • 1,980
  • 1
  • 15
  • 29
artem
  • 46,476
  • 8
  • 74
  • 78
  • From [HTML specs](https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier) perspective, module specifiers without an extension are allowed. – Kaiido Mar 21 '19 at 23:43
  • 2
    > "From HTML specs perspective, module specifiers without an extension are allowed." That's a bit misleading: The HTML spec doesn't allow extension searching. It will only find a referenced resource if the resource itself doesn't have an extension. So in the case of ./file it will only find something that's literally called ./file but not ./file.mjs or anything else. – Jan Olaf Krems Feb 07 '20 at 19:10
  • 2
    My current experience with the plain typescript compiler doesn't match the description here; it isn't adding '.js' to the `import {x} from './file'` in the compiled code. It appears that if you write `import {x} from './file.js'` then it will indeed still use `file.ts` for compilation and type checking, and generate js with the unchanged filename such that it does then work. – PeterT Apr 13 '21 at 10:23
  • > import 'lodash' could resolve to ./node_modules/lodash/index.mjs . What happens if that folder has both index.mjs AND index.js ? – Panu Logic Apr 17 '23 at 17:11
  • this is determined by "main" and "exports" settings in lodash `package.json` file, as described in the "package entry points" section in the [documentation](https://nodejs.org/api/packages.html) – artem Apr 18 '23 at 11:33
3

@PeterT description worked with me. But still I like to explain more. In development all the configurations and bundling set by webpack so we dont need to do any such thing. When we learn modules by our own with typescript and want to experiment with basic typescript compiler, definetyly we need to do like this.

in the typescript file imports include the .js extension in the import section like this

import {x} from 'X.js';//remember include '.js'