Skip to content

URL version of process.cwd() #49273

Description

@LiviaMedeiros

What is the problem this feature will solve?

Right now we don't have convenient way to get current working directory in URL form such as file:///path/to/current/working/directory/.

Having easy access to this form would allow to resolve local paths using WHATWG URL constructor, i.e. new URL('../path/to/file', cwdURL), without need to import path and worry about platform-specific separators.

Getting this form in userspace is not very convenient:

// results in URL without trailing slash, indicating that it's directory and allowing to use it directly as baseURL
const cwdURL = url.pathToFileURL(process.cwd());

// appending pathname suffix to full href is semantically wrong
const cwdURL = url.pathToFileURL(process.cwd()).href + '/';

// triggers pathname setter which requires reparsing URL again
const cwdURL = url.pathToFileURL(process.cwd());
cwdURL.pathname += '/';

// three imports for one value, including `path` which we may want to avoid
const cwdURL = url.pathToFileURL(process.cwd() + path.sep);

// very bad for multiple reasons but tempting enough to appear in the wild :(
const cwdURL = `file://${process.cwd()}/`;

What is the feature you are proposing to solve the problem?

  • support URL instances with file: protocol in process.chdir(directory).
  • add process.cwdURL() that returns URL instance or string.
  • add resolve(...urls) method that would construct new absolute URL on top of cwd if all provided urls are relative. url.resolve() is not vacant and has different behaviour, so it should have different name or place.

Better naming suggestions are very welcome.

What alternatives have you considered?

  • userland module.
  • adjustments to parsing URLs with file: protocol that would resolve relative pathname against cwd instead of root, i.e.:
// current behaviour
new URL('file:./file.txt').href === 'file:///file.txt'

// proposed behaviour
new URL('file:./file.txt').href === 'file:///path/to/current/working/directory/file.txt'

Activity

  1. added
    processIssues and PRs related to the process subsystem.
    whatwg-urlIssues and PRs related to the WHATWG URL implementation.
    on Aug 21, 2023
  2. aduh95 commented on Aug 21, 2023

    @aduh95
    Contributor

    What not use paths? Do you have a specific use case in mind?

  3. LiviaMedeiros commented on Aug 22, 2023

    @LiviaMedeiros
    MemberAuthor

    In general,

    1. Cases when we have to use urls. For example,
    const settingsLocation = new URL('../.config/settings.json', env.MYBASEDIR ?? cwdURL());
    validateOrMutate(settingsLocation);
    const settings = await import(settingsLocation, { assert: { type: 'json' } });
    • any third-party module that accepts only URL, e.g. for compatibility with browsers
    • perhaps, eventually Fetch API
    1. Cases when we want to use urls. For example, if
    • we deal not only with local filesystem, e.g. to be polymorphic with file:///usr/portage/net-dialup/ and rsync://winchester.local/portage/net-dialup/
    • we want to make sure that on non-posix OS relative paths are not misinterpreted on input and not generated with backslashes on output
    • we want to write API that accepts urlstrings, and we don't want heuristics to determine if it's URL or path
    1. All of /pathToFileURL\((process\.)?cwd\(/, js:file://${cwd}, ts:file://${cwd}
    2. ESM in Node.js core: lib/repl.js lib/internal/process/pre_execution.js lib/internal/modules/esm/resolve.js lib/internal/modules/esm/loader.js

    In most of these, having URL instance that is resolved against cwd would allow avoid conversion (or even double conversion) between URL and path and having a mix of these in code (which can easily become a bug farm, as many operations keep working until there is directory with %/?/#, or URL with hash, or the code runs on Windows).


    As specific usecase, let's say, I want to read a file defined by URL (can be string, can be relative), in a way like

    const buffer = myFsReadFileSyncByURL('./path/to/file.bin?position=30&length=8#btwAmRelativeURL');
    const text = await (await myFetch('path/to/file%20with%20spaces.txt')).text();

    It's simple with absolute URL, but there's no concise way to resolve relative URL into it: WHATWG URL standard doesn't support relative URLs (at least, yet), url.fileURLToPath('file:./path') resolves against / or throws if protocol is omitted.

    Also as a matter of fact, even if we want to switch context from URLs to paths. we don't have relativeFileURLtoRelativePath() utility either.

  4. anonrig commented on Aug 22, 2023

    @anonrig
    Member

    I think what you want is similar to Rust's Path struct where you can easily mutate the pathname, get URL components and change them without sacrificing the DX and performance.

  5. github-actions commented on Feb 19, 2024

    @github-actions
    Contributor

    There has been no activity on this feature request for 5 months and it is unlikely to be implemented. It will be closed 6 months after the last non-automated comment.

    For more information on how the project manages feature requests, please consult the feature request management document.

  6. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Feb 19, 2024
  7. github-actions commented on Mar 21, 2024

    @github-actions
    Contributor

    There has been no activity on this feature request and it is being closed. If you feel closing this issue is not the right thing to do, please leave a comment.

    For more information on how the project manages feature requests, please consult the feature request management document.

  8. karlhorky commented on Apr 5, 2024

    @karlhorky
    Contributor

    FWIW I also ran into this issue today with using process.cwd() as part of a path passed to dynamic import(), and the error was pretty confusing (and only on Windows);

    $ pnpm eslint . --max-warnings 0
      
    Oops! Something went wrong! :(
    ESLint: 8.57.0
    Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'
        at throwIfUnsupportedURLScheme (node:internal/modules/esm/load:239:11)
        at defaultLoad (node:internal/modules/esm/load:130:3)
        at ModuleLoader.load (node:internal/modules/esm/loader:409:13)
        at ModuleLoader.moduleProvider (node:internal/modules/esm/loader:291:56)
        at new ModuleJob (node:internal/modules/esm/module_job:65:26)
        at #createModuleJob (node:internal/modules/esm/loader:303:17)
        at ModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:260:34)
        at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:241:17)
        at async ModuleLoader.import (node:internal/modules/esm/loader:328:23)
    Error: Process completed with exit code 1.

    Doesn't show any user code in stack trace. In my case, I was using the dynamic import inside of a eslint.config.js (ESLint Flat Config) file:

    // ✅ Working on macOS and Linux, 💥 Throws error on Windows
    await import(`${process.cwd()}/package.json`, {
      assert: { type: 'json' },
    });

    Eventually I guessed my way to figure out it was the process.cwd() failing only on Windows inside the dynamic import(), and also used pathToFileURL from node:url:

    +import { pathToFileURL } from 'node:url';
    
    -await import(`${process.cwd()}/package.json`, {
    +await import(pathToFileURL(`${process.cwd()}/package.json`).href, {
      assert: { type: 'json' },
    });
  9. karlhorky commented on Apr 5, 2024

    @karlhorky
    Contributor

    @aduh95 @anonrig is there any Node.js implementer interest in making this easier to fall into the pit of success on Windows?

    If so, maybe this issue could be reopened?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues requesting new Node.js features.processIssues and PRs related to the process subsystem.staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.whatwg-urlIssues and PRs related to the WHATWG URL implementation.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions