You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ES Module loading with abolute path fails on windows #31710
import other from "C:\\prog\\node\\genast\\other.js"
console.log(other)
It errors out with:
internal/modules/run_main.js:54
internalBinding('errors').triggerUncaughtException(
^
Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only file and data URLs are supported by the default ESM loader
at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:33:11)
at Loader.resolve (internal/modules/esm/loader.js:85:40)
at Loader.getModuleJob (internal/modules/esm/loader.js:188:40)
at ModuleWrap. (internal/modules/esm/module_job.js:42:40)
at link (internal/modules/esm/module_job.js:41:36) {
code: 'ERR_UNSUPPORTED_ESM_URL_SCHEME'
}
Using forward- och backward slashes doesn't make a difference.
How often does it reproduce? Is there a required condition?
100%
What is the expected behavior?
Absolute includes should work the same as relative
What do you see instead?
Additional information
The same issue is seen with both static ES imports and dynamic ES imports.
The specifier of an import statement is the string after the from keyword, e.g. 'path' in import { sep } from 'path'. Specifiers are also used in export from statements, and as the argument to an import() expression.
There are four types of specifiers:
Bare specifiers like 'some-package'. They refer to an entry point of a package by the package name.
Deep import specifiers like 'some-package/lib/shuffle.mjs'. They refer to a path within a package prefixed by the package name.
Relative specifiers like './startup.js' or '../config.mjs'. They refer to a path relative to the location of the importing file.
Absolute specifiers like 'file:///opt/nodejs/config.js'. They refer directly and explicitly to a full path.
Bare specifiers, and the bare specifier portion of deep import specifiers, are strings; but everything else in a specifier is a URL.
Only file: and data: URLs are supported. A specifier like 'https://example.com/app.js' may be supported by browsers but it is not supported in Node.js.
Specifiers may not begin with / or //. These are reserved for potential future use. The root of the current volume may be referenced via file:///.
I expected the same to be true on windows, hence the issue report :)
Thanks @devsnek as well!
In the issue I posted over on "jest" about this, they said you could use require('url').pathToFileURL(specifier).href to turn a path into a "file url". But it seems a bit weird if this is required for windows but not macos (and presumably linux). A lot of the packages I depend on will probably not know about this
Reacted by Kiran Pawar, Nikolay Kuchumov, Ronan McCarter and Noam Gaash
$ echo'export default "hello!";'> file.mjs
$ node -e 'import(require.resolve("./file.mjs")).then(console.log, console.error)'(node:3036) ExperimentalWarning: The ESM module loader is experimental.Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only file and data URLs are supported by the default ESM loader at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:33:11) at Loader.resolve (internal/modules/esm/loader.js:85:40) at Loader.getModuleJob (internal/modules/esm/loader.js:188:40) at Loader.import (internal/modules/esm/loader.js:163:28) at importModuleDynamically (internal/modules/cjs/loader.js:1094:27) at exports.importModuleDynamicallyCallback (internal/process/esm_loader.js:37:14) at Object.<anonymous> (C:\Users\IEUser\script.js:1:16) at Module._compile (internal/modules/cjs/loader.js:1151:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1171:10) at Module.load (internal/modules/cjs/loader.js:1000:32) { code: 'ERR_UNSUPPORTED_ESM_URL_SCHEME'}
With url.pathToFileURL it works, so we can use that in Jest. I'd still consider this a bug in node, though
Reacted by jonerer, Jaid, Ronan McCarter, Dimitri POSTOLOV, Pierre Cavin and Mikhail Hozhy
From a user perspective it's really weird that absolute paths work for some OSes, but not all. Would it make sense to throw on absolute paths without file:// on mac and linux as well?
Reacted by Bo Lingen, Ronan McCarter, Wynn Slater and Philipp Spiess
Yeah, I've changed to use that in Jest now, but it's still odd to me that it behaves differently across OSes, even though pathToFileURL fixes it. You have to actually test on windows to notice absolute paths doesn't work (in our case, we only tested windows on Node 12, while our test only runs on Node 13), then find about that API (followed by either fixing your own code, or reporting it to some library).
Maybe the error message could include something about passing the file path provided through pathToFileURL? Should hopefully mean less googling for people hitting it
Reacted by joão melo, Owen Buckley, Ronan McCarter and Wynn Slater
Yeah, I understand why it happens, I just disagree with the behavior that absolute paths are treated differently between platforms, and would prefer it to just accept either relative paths or absolute paths that has the file protocol to avoid the foot gun of code working fine on one platform and not the other.
That ship might have sailed though 🙂
Reacted by Marie Hoeger, Dariusz Kobuszewski, joão melo, Steven, pushkine, Jaid, Ren Hiyama, David Chen, Owen Buckley, Ronan McCarter and 7 more
I 100% agree with passing the path through url.pathToFileURL first (that's the fixed I've applied in Jest), but I think it should be required on all platforms, not just Windows.
Reacted by Marie Hoeger, George Walker, joão melo, Steven, Ronan McCarter and Alex Yang
So to summarize and clarify, ESM importing right now works like this:
relative paths:
works on mac/linux
works on windows
absolute paths:
works on mac/linux
doesn't work on windows
"local file urls"
works on mac/linux
works on windows
This issue report is about the inconsistency regarding "absolute paths".
For my money, I would love to have absolute paths work on windows as well. It only makes sense that a function to include other files can actually handle paths. At least when running in a node.js environment.
I understand the difficulties differentiating between a windows absolute path and an url, since the drive letter could be confused with a URL scheme. And single-letter schemes seem to be valid according to this RFC: https://tools.ietf.org/html/rfc3986#section-3.1 . But I think it's safe to assume that introducing single-letter URL schemes in a world where windows exists is not likely to happen
I'm not sure how we could detect the intent of someone when they typed it, since that's all that separates an absolute path from a relative path absolute url string.
It would be nice to be able to load an absolute path... in this case I don't have a way to set the current working directory, and file:///c:/... or file://c:/... do not work. It ends up prepending the current path (which again I don't have control over) and attempting to load like c:\tmp\file:\c:\ for node file:///c:/tmp/shell/node_modules/... I just happen to be in c:\tmp when testing this, but that is not likely to be the path.
Activity
devsnek commentedon Feb 10, 2020
You'd need to write
file:///c:/x/y/zinstead ofc:/x/y/zkiranpwr1260 commentedon Feb 11, 2020
As per node.js documentation:
The specifier of an import statement is the string after the from keyword, e.g. 'path' in import { sep } from 'path'. Specifiers are also used in export from statements, and as the argument to an import() expression.
There are four types of specifiers:
Bare specifiers like 'some-package'. They refer to an entry point of a package by the package name.
Deep import specifiers like 'some-package/lib/shuffle.mjs'. They refer to a path within a package prefixed by the package name.
Relative specifiers like './startup.js' or '../config.mjs'. They refer to a path relative to the location of the importing file.
Absolute specifiers like 'file:///opt/nodejs/config.js'. They refer directly and explicitly to a full path.
Bare specifiers, and the bare specifier portion of deep import specifiers, are strings; but everything else in a specifier is a URL.
Only file: and data: URLs are supported. A specifier like 'https://example.com/app.js' may be supported by browsers but it is not supported in Node.js.
Specifiers may not begin with / or //. These are reserved for potential future use. The root of the current volume may be referenced via file:///.
jonerer commentedon Feb 11, 2020
@kiranpwr1260 thanks!
It works on mac os though. Maybe not the same way you posted, but it works according to expectation.
On macos, if you do this:
console.log('wep')node index.jsOutputs, as expected,
wepI expected the same to be true on windows, hence the issue report :)
Thanks @devsnek as well!
In the issue I posted over on "jest" about this, they said you could use
require('url').pathToFileURL(specifier).hrefto turn a path into a "file url". But it seems a bit weird if this is required for windows but not macos (and presumably linux). A lot of the packages I depend on will probably not know about thisSimenB commentedon Feb 11, 2020
Yeah, I think this is either a bug on macos & linux or windows.
Mac and Linux (alpine docker image, to be precise):
Windows:
With
url.pathToFileURLit works, so we can use that in Jest. I'd still consider this a bug in node, thoughdevsnek commentedon Feb 11, 2020
the issue is that
C:/is ambiguous with a URL. (likeHTTP:/) and we use URLs in the loader.SimenB commentedon Feb 11, 2020
From a user perspective it's really weird that absolute paths work for some OSes, but not all. Would it make sense to throw on absolute paths without
file://on mac and linux as well?devsnek commentedon Feb 11, 2020
you should use https://nodejs.org/api/url.html#url_url_pathtofileurl_path
SimenB commentedon Feb 11, 2020
Yeah, I've changed to use that in Jest now, but it's still odd to me that it behaves differently across OSes, even though
pathToFileURLfixes it. You have to actually test on windows to notice absolute paths doesn't work (in our case, we only tested windows on Node 12, while our test only runs on Node 13), then find about that API (followed by either fixing your own code, or reporting it to some library).Maybe the error message could include something about passing the file path provided through
pathToFileURL? Should hopefully mean less googling for people hitting itExE-Boss commentedon Feb 18, 2020
This is because
C:/path/to/module.jsgets parsed as:And Node doesn’t know how to deal with a URL with a scheme of
c:.What you want is
file:///C:/path/to/module.js:SimenB commentedon Feb 18, 2020
Yeah, I understand why it happens, I just disagree with the behavior that absolute paths are treated differently between platforms, and would prefer it to just accept either relative paths or absolute paths that has the
fileprotocol to avoid the foot gun of code working fine on one platform and not the other.That ship might have sailed though 🙂
devsnek commentedon Feb 18, 2020
@SimenB it doesn't accept paths, it accept urls. like i said, the correct thing to do if you have a path is to use pathToFileURL.
SimenB commentedon Feb 18, 2020
Absolute paths (either unix or windows style) aren't valid URLs, though.
(same behavior in Chrome)
I 100% agree with passing the path through
url.pathToFileURLfirst (that's the fixed I've applied in Jest), but I think it should be required on all platforms, not just Windows.jonerer commentedon Feb 18, 2020
So to summarize and clarify, ESM importing right now works like this:
This issue report is about the inconsistency regarding "absolute paths".
For my money, I would love to have absolute paths work on windows as well. It only makes sense that a function to include other files can actually handle paths. At least when running in a node.js environment.
I understand the difficulties differentiating between a windows absolute path and an url, since the drive letter could be confused with a URL scheme. And single-letter schemes seem to be valid according to this RFC: https://tools.ietf.org/html/rfc3986#section-3.1 . But I think it's safe to assume that introducing single-letter URL schemes in a world where windows exists is not likely to happen
devsnek commentedon Feb 18, 2020
@SimenB they aren't valid absolute urls, but they are valid as relative urls (https://url.spec.whatwg.org/#path-absolute-url-string), which is why they're accepted in
import.I'm not sure how we could detect the intent of someone when they typed it, since that's all that separates an absolute path from a relative path absolute url string.
19 remaining items
fix: use file:// for module resolution of Windows absolute paths
fix: use file:// for module resolution of Windows absolute paths
fix: use file:// for module resolution of Windows absolute paths
fix: There's a difference in Node's handling of imports across OSes. …
fix: There's a difference in Node's handling of imports across OSes. …
ES Module loading with abolute path fails on Windows unless it's conv…
fix(build): Windows build
fix(build): Windows build
fix: loading absolute path fails on windows
fix: loading absolute path fails on windows
d3x0r commentedon Nov 17, 2023
It would be nice to be able to load an absolute path... in this case I don't have a way to set the current working directory, and
file:///c:/...orfile://c:/...do not work. It ends up prepending the current path (which again I don't have control over) and attempting to load likec:\tmp\file:\c:\fornode file:///c:/tmp/shell/node_modules/...I just happen to be in c:\tmp when testing this, but that is not likely to be the path.fix(amazonq): avoid ERR_UNSUPPORTED_ESM_URL_SCHEME error when loading…
fix(typesync): make dynamic import work on Windows
fix(typesync): make dynamic import work on Windows
feat: Native ESM Support (#68)