Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a variable or func to get the path of the current script, like __dirname in Node.js #1286

Closed
manyuanrong opened this issue Dec 6, 2018 · 31 comments

Comments

@manyuanrong
Copy link
Contributor

When the entry script is under the subdirectory, reading the relative file under the subdirectory is unsuccessful.
In Node.js, you can get the directory where the script is located by __dirname.
And __file__ can also be used in Python

@zhmushan
Copy link
Contributor

zhmushan commented Dec 6, 2018

Hi, I am not sure if this is what you need.

import { cwd } from 'deno'
console.log(cwd())

@kevinkassimo
Copy link
Contributor

kevinkassimo commented Dec 6, 2018

Actually no, cwd is just the working directory...
Could be injected by TypeScript compiler

@manyuanrong
Copy link
Contributor Author

Hi, I am not sure if this is what you need.

import { cwd } from 'deno'
console.log(cwd())

Unfortunately, cwd() does not achieve the same effect.
Assume you have the following files and directories:

~/demo/test/test.ts
~/demo/test/test.txt

When I execute deno test/test.ts under ~/demo,
cwd() in ~/demo/test/test.ts will output ~/demo, not ~/demo/test.

This is a common use, for example, when I use Deno to write some tool scripts

@kevinkassimo
Copy link
Contributor

Sounds like injecting through TS compiler should work. There is also a pitfall that the actual running files are loaded from cache under DENO_PATH and thus different from the expected TS file path. Need to differentiate the two.

@kitsonk Does this look like a good place for injection? (on my phone so cannot really verify if it would work)

@kitsonk
Copy link
Contributor

kitsonk commented Dec 6, 2018

Sounds like injecting through TS compiler should work.

No, in my opinion. We are working on #975. When that lands, coupled with #1190 then in a lot of cases the TypeScript compiler won't get involved.

Rust "knows" where the original file is located, even if it is loading from the cache, but even then it is tricky when dealing with remote modules.

Basing code dealing with the relative location of the module that the code is running in is rather dangerous. It can also be super fragile, what if the module get moved around. Why would you want to "hard code" that into the logic of the module?

While it would be Ryan's call, I would personally want to try to avoid this at all costs unless there are problems that just can't be solved. Don't want to handle people a 👣 🔫 to author fragile code if we can avoid it.

All file creation/read/access APIs that are exposed would be relative to the cwd() anyways.

@ry
Copy link
Member

ry commented Dec 7, 2018

I think it would be nice if we could do

import { uint8Array } from "./style.css";

that is, statically resolving and including non-javascript files. Maybe that negates the need to have __dirname.

I'm not necessarily against __dirname (it's certainly useful) but I think it would be nice if we could avoid it. Let's add the above feature before doing __dirname. If we can avoid __dirname that means code is that much more context-independent - which is good for maintenance and reuse-ability .

I'm definitely not doing the two underscore thing again tho. We will use DIRNAME (or something)

@kitsonk
Copy link
Contributor

kitsonk commented Dec 7, 2018

@ry we would/could handle any media type in the compiler.

We already support .json and associated media types in the compiler. If we assume anything else is "text" ish... we would potentially just "compile" down to something like this:

export function toUint8Array(): Uint8Array {
  return deno.getUint8Array("./style.css");
}

export function toString(): string {
  return (new TextDecoder()).decode(deno.getUint8Array("./style.css"));
}

I think we would have to generate that "shell" module which would be loaded in the runtime which will op back out to Rust to get the resource. Rust would use the module resolution logic, which would negate the need for the module to know where it sits. Doing some sort of "getter" as an export of a module is awkward since export = is not an ESM construct. So it does mean that you would have to do something like this:

import * as styleCss from "./style.css";

const value = styleCss.toUint8Array();

@Janpot
Copy link

Janpot commented Dec 8, 2018

Isn't this what import.meta.url was intended for?

@ry
Copy link
Member

ry commented Dec 8, 2018

@Janpot thanks - I didn’t know about that

@smith
Copy link

smith commented Dec 9, 2018

@Janpot I think we might need to tweak the tsconfig options for this to work as intended:

1:13 - error TS1343: The 'import.
meta' meta-property is only allowed using 'ESNext' for the 'target' and
'module' compiler options.

1 console.log(import.meta);

@kitsonk
Copy link
Contributor

kitsonk commented Dec 9, 2018

@smith this would be resolved when we move to native ESM which is soon. #975

@Lonniebiz
Copy link

Lonniebiz commented Jan 25, 2019

__dirname doesn't work in node.js either when you are using ES Modules. Here's how you get around it in node.js :

import path from 'path';
const __dirname = path.dirname(new URL(import.meta.url).pathname);

After this, I could use __dirname in the node script, but eslint couldn't parse any use of the import.meta object, because eslint doesn't support stage 3 proposals:
https://stackoverflow.com/questions/54337576/eslint-import-meta-causes-fatal-parsing-error

@bartlomieju
Copy link
Member

You can now retrieve URL of script with import.meta.url

@ry ry closed this as completed Feb 12, 2019
@npup
Copy link

npup commented Apr 20, 2020

Hesitated to write here this but all my searches led me to this thread.

What about finding the directory of the current script then? That is what I want most of the time.

The import.meta.url gives a file:///... to the file itself (of course), and I haven't found very ergonomic ways to use it to then carry out what path.join(___dirname, "static-files") used to do.

@zhmushan
Copy link
Contributor

@npup import.meta doesn't work well on win10, maybe this is a bug.
ref: #4647

@SyrupThinker
Copy link
Contributor

@npup There's the path module which you could use.

@zhmushan
Copy link
Contributor

@SyrupThinker path.join(import.meta.url, "path") cannot be used directly on Win10

@nayeemrmn
Copy link
Collaborator

@npup new URL("static-files", import.meta.url).pathname.

@npup
Copy link

npup commented Apr 20, 2020 via email

@seishun
Copy link
Contributor

seishun commented Apr 21, 2020

@nayeemrmn that gives a path that looks like "/C:/Users/seishun/project/static-files", which Deno.readFileSync doesn't accept.

@seishun
Copy link
Contributor

seishun commented Apr 21, 2020

Here's a hacky but cross-platform way:

import { createRequire } from "https://deno.land/std/node/module.ts";
const require = createRequire(import.meta.url);
console.log(require.resolve("./static-file"));

@nayeemrmn
Copy link
Collaborator

@seishun new URL("static-files", import.meta.url).pathname.slice(Deno.build.os == "win") 🤷‍♂️

@Vandivier
Copy link

@nayeemrmn ...slice(Deno.build.os == "win") isn't typed correctly. I suppose the false case should evaluate numerically to 0. Is the true case 1?

@Vandivier
Copy link

related to my above comment: #4915

@nayeemrmn
Copy link
Collaborator

@zhmushan @seishun Update! As of Deno 1.1.0, thanks to @actual-size's work in #5990, FS functions support file URLs so you can do the following:

console.log(Deno.statSync(import.meta.url)); // FileInfo { ... }
console.log([...Deno.readDirSync(new URL("static-files", import.meta.url))]); // [ ... ]

@zhmushan
Copy link
Contributor

Thanks for your mention, this is very useful for me!

@han-tyumi
Copy link

From my testing, it seems like import.meta.url does not work as expected when bundling. It will instead reference the original file that was bundled. I could create a bug if this is not intended. If intended, then I'd be looking for another option to make my script portable so that it can use a directory relative to the location of it.

@kitsonk
Copy link
Contributor

kitsonk commented Sep 21, 2020

@han-tyumi there is already an issue for that: #6344

@martin-braun
Copy link

martin-braun commented Feb 1, 2022

import __ from "https://deno.land/x/dirname/mod.ts";
import { IMakeLoc } from "https://deno.land/x/dirname/types.ts";
const { __filename, __dirname }: IMakeLoc = __(import.meta)

is a way to do it, but I really don't like to depend on an external module for something like this.

@mindon
Copy link

mindon commented Aug 21, 2024

import { fromFileUrl, dirname } from "jsr:@std/path";
console.log(dirname(fromFileUrl(import.meta.url)));

@marvinhagemeister
Copy link
Contributor

There is also this:

const __filename = import.meta.filename;
const __dirname = import.meta.dirname;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests