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?
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.
i encountered the same problem, and i SOLVED "easily", this way : URL REWRITING, what do i mean ? i'm gonna explain that from the beginning !
Here is the story, from compiled Typescript code, i wanted to obtain some ES6 code(target es2015 in tsconfig.json), in order to keep in the final code the original syntaxes: import/export that i've written in Typescript ( indeed at final, i did not want to use Module loaders for ES5 !! Because it's old school...).
The thing is that, when Typescript generates the ES6, it completely keeps intact the syntaxes : import .. from "./.../MyJSFile", so it keeps it without the ".js" extension !! And of course, in Typescript code, you can't mention the extension on an import syntax ! (it assumes it to be ".ts").
So now, i've got like you, the ES6 code generated, and containing import syntaxes that DOESN'T mention the ".js" extension of the imported files. So what is the problem ?
The problem is :
In order to execute such code in a browser, because it uses ES6 Modules
you HAVE TO run it from a WEB SERVER (no matter which) !
BUT ! any Web Server won't find the files you want to import
(import ... from "./.../MyJSFile"), because without extension, it says, well
this resource doesn't exist ! and indeed it doesn't !! BUT what we know is
that the resource "./.../MyJSFile.JS" EXISTS !
So ?? You see me coming ?
YOU JUST HAVE TO WRITE : URL REWRITING RULES FOR THE WEB SERVER, and that's
what i did to solve ! I did it with :
- an APACHE WEB SERVER (that has a directive : AllowOverride All ,
in order to allow URL rewriting !!).
and then, i just created an .htaccess file at the root of my
localhost site, in which i tell him:
If the requested resource doesn't exist, then try to find
(by URL Rewriting), the same resource name BUT, with a ".js" extension
at the end of it ! AND IT WORKS FINE !! Here is my .htaccess in its
simplest working form :
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)(/[A-Za-z0-9_]+)$ $1$2.js [L]
AND THAT's really all you have to do ! with an Apache Server.
So keep in mind, whatever is the Web Server, the idea is : URL REWRITING !
I ALSO USED THE SAME PRINCIPLE WITH a NODE.JS WEB SERVER that i've created, here is its full tested code, it works absolutely fine (nodeJsAsWebServer.js) :
//In terminal, just run : node nodeJsAsWebServer
//to launch this below Web Server. (CTRL+C to stop) .
//
//And then in the browser, just invoke your ES6 code
//(via normal html call),
//on this domain : localhost:555
//------- nodeJsAsWebServer.js ---------
const oHttpTool = require('http');
const oFilesTool = require('fs');
const oPathesTool = require('path');
const oMIMETypes = {
'html': 'text/html',
'css': 'text/css',
'js': 'text/javascript',
'json': 'application/json',
'png': 'image/png',
'jpg': 'image/jpeg',
'ico': 'image/x-icon',
'wav': 'audio/wav',
'mp3': 'audio/mpeg',
'pdf': 'application/pdf',
'doc': 'application/msword'
};
let oMyWebServer = oHttpTool.createServer(function (req, res) {
console.log("\n\n=========================================================");
console.log("=========================================================\n");
let sSubUrl = req.url;
console.log("Client code asks for: "+sSubUrl);
let sFileExtension = oPathesTool.parse(sSubUrl).ext.substr(1);
let sMIMEType = oMIMETypes[sFileExtension];
console.log("Asked MIMEType="+sMIMEType);
let sFileHDDPath = __dirname; //Real path on server hard disk
let sFileToRead = sFileHDDPath+sSubUrl;
console.log("FileToRead="+sFileToRead);
oFilesTool.exists(sFileToRead, function (pbFileExists) {
if(!pbFileExists) {
console.log(` - File NOT FOUND on the server! -`);
if (sFileExtension==="") { //If the requested URL doesn't have any extension !
let sNewUrl = sSubUrl+".js"; //<<<<<<<<<<<< URL Rewriting
console.log(" REDIRECTION ...(sNewUrl='"+sNewUrl+"'; ldUrl='"+sSubUrl+"')");
res.writeHead(301, {Location:sNewUrl});//Redirect, we try to load this newURL
res.end();
} else { //if the URL already had an extension.
res.statusCode = 404;
res.end();
}
} else { //Normal case, when the requested resource exists.
oFilesTool.readFile(sFileToRead, function(poError, poData){
if(poError){
res.statusCode = 500;
console.log("ERROR getting file content !!", poError);
res.end();
} else { //We send the file whole content and with a header.
res.setHeader('Content-type', sMIMEType );
res.end(poData); //<<Also works for Binary files such as Image, PDF, etc..
}
});
}
});
});
let iPort=555; //<<< choose your port on localhost.
console.clear();
console.log("Node Web Server listening on localhost:"+iPort+" ... (CTRL+C to stop)");
oMyWebServer.listen(iPort);