Initial commit

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
dinlo
2026-05-31 18:44:04 +08:00
commit 436a9631fc
8616 changed files with 1389957 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
# cross-dirname
[Node.js](https://nodejs.org) + [Gjs](https://gjs.guide/) + [Deno](https://deno.land/) module that returns the current script dirname and filename. Similar to `__dirname` and `__filename` but also works in CommonJs and ES modules.
## Installation
On Node.js and GJS you can install the package as with NPM:
```
npm install cross-dirname --save
```
On Deno you just need to import this package:
```ts
import { getDirname, getFilename } from 'https://deno.land/x/cross_dirname/mod.ts';
```
## Usage
Please do not use `getDirname` and `getFilename` in nested other methods, instead always use them in the root of your file, otherwise it may return wrong results.
### Node.js ESM
```js
// /path/to/the/script.mjs
import { getDirname, getFilename } from 'cross-dirname'
console.log(getDirname()) // outputs "/path/to/the"
console.log(getFilename()) // outputs "/path/to/the/script.mjs"
```
### Node.js CJS
```js
// /path/to/the/script.cjs
const { getDirname, getFilename } = require('cross-dirname');
console.log(getDirname() === __dirname) // true
console.log(getFilename() === __filename) // true
```
### Deno
```ts
// /path/to/the/script.ts
import { getDirname, getFilename } from 'https://deno.land/x/cross_dirname@v0.0.4/mod.ts';
console.log(getDirname()); // outputs "/path/to/the"
console.log(getFilename()); // outputs "/path/to/the/script.ts"
```
### GJS
You can use NPM packages in GJS with a bundler like [esbuild](https://esbuild.github.io/).
Take a look at the examples for an [GJS + esbuild example](/examples/gjs), you can start the example like this:
```bash
# Install dev dependencies
npm install
# Go to the example
cd examples/gjs
# Bundle src/index.js
node esbuild.mjs
# Run the bundled index.js
gjs -m index.js
```
### Examples
You can run the examples with
```bash
npm install
npm run build
deno run ./examples/deno/index.ts
# -> /.../examples/deno
node ./examples/node/index.cjs
# -> /.../examples/node
node ./examples/node/index.mjs
# -> /.../examples/node
node ./examples/gjs/esbuild.mjs
gjs -m ./examples/gjs/index.js
# -> /.../examples/gjs
```
### Contributions
Contributions for more platforms are welcome :)
### Tests
This module has been tested on the following platforms:
| Runtime | Type | Platform | State |
|---------|--------|----------|----------|
| Node.js | CJS | Linux | ✔ |
| Node.js | CJS | MacOS | ✔ |
| Node.js | CJS | Windows | ✔ |
| Node.js | ESM | Linux | ✔ |
| Node.js | ESM | MacOS | ✔ |
| Node.js | ESM | Windows | ✔ |
| Deno | ESM | Linux | ✔ |
| Deno | ESM | MacOS | ✔ |
| Deno | ESM | Windows | ✔ |
| Gjs | ESM | Linux | ✔ |
| Gjs | ESM | MacOS | UNTESTED |
| Gjs | ESM | Windows | UNTESTED |
| Chrome | ESM | Browser | ✔ |
| Chrome | CJS | Browser | ✔ |
You can run all tests with:
```
npm run test
```
Or the tests for a special runtime:
```
npm run test:node
npm run test:deno
npm run test:gjs
npm run test:browser´
```
+76
View File
@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFilename = exports.getDirname = void 0;
const DIRNAME_POSIX_REGEX = /^((?:\.(?![^\/]))|(?:(?:\/?|)(?:[\s\S]*?)))(?:\/+?|)(?:(?:\.{1,2}|[^\/]+?|)(?:\.[^.\/]*|))(?:[\/]*)$/;
const DIRNAME_WIN32_REGEX = /^((?:\.(?![^\\]))|(?:(?:\\?|)(?:[\s\S]*?)))(?:\\+?|)(?:(?:\.{1,2}|[^\\]+?|)(?:\.[^.\\]*|))(?:[\\]*)$/;
const EXTRACT_PATH_REGEX = /@?(?<path>[file:\/\/]?[^\(\s]+):[0-9]+:[0-9]+/;
const WIN_POSIX_DRIVE_REGEX = /^\/[A-Z]:\/*/;
const pathDirname = (path) => {
let dirname = DIRNAME_POSIX_REGEX.exec(path)?.[1];
if (!dirname) {
dirname = DIRNAME_WIN32_REGEX.exec(path)?.[1];
}
if (!dirname) {
throw new Error(`Can't extract dirname from ${path}`);
}
return dirname;
};
const getPathFromErrorStack = () => {
let path = '';
const stack = new Error().stack;
if (!stack) {
console.warn("Error has no stack!");
return path;
}
// Node.js
let initiator = stack.split('\n').slice(4, 5)[0];
// Other
if (!initiator) {
initiator = stack.split('\n').slice(3, 4)[0];
}
if (initiator) {
path = EXTRACT_PATH_REGEX.exec(initiator)?.groups?.path || '';
}
if (!initiator || !path) {
console.warn("Can't get path from error stack!");
}
return path;
};
const getPath = () => {
let path = getPathFromErrorStack();
// Remove protocol
const protocol = "file://";
if (path.indexOf(protocol) >= 0) {
path = path.slice(protocol.length);
}
// Transform to win32 path
if (WIN_POSIX_DRIVE_REGEX.test(path)) {
path = path.slice(1).replace(/\//g, '\\');
}
return path;
};
/**
* Cross platform implementation for `__dirname`.
*
* @note Please do not use this method in nested other methods,
* instead always use it in the root of your file, otherwise it may return wrong results.
* @returns What `__dirname` would return in CJS
*/
const getDirname = () => {
let path = getPath();
const dirname = pathDirname(path);
return dirname;
};
exports.getDirname = getDirname;
/**
* Cross platform implementation for `__filename`.
*
* @note Please do not use this method in nested other methods,
* instead always use it in the root of your file, otherwise it may return wrong results.
* @returns What `__filename` would return in CJS
*/
const getFilename = () => {
let filename = getPath();
return filename;
};
exports.getFilename = getFilename;
+71
View File
@@ -0,0 +1,71 @@
const DIRNAME_POSIX_REGEX = /^((?:\.(?![^\/]))|(?:(?:\/?|)(?:[\s\S]*?)))(?:\/+?|)(?:(?:\.{1,2}|[^\/]+?|)(?:\.[^.\/]*|))(?:[\/]*)$/;
const DIRNAME_WIN32_REGEX = /^((?:\.(?![^\\]))|(?:(?:\\?|)(?:[\s\S]*?)))(?:\\+?|)(?:(?:\.{1,2}|[^\\]+?|)(?:\.[^.\\]*|))(?:[\\]*)$/;
const EXTRACT_PATH_REGEX = /@?(?<path>[file:\/\/]?[^\(\s]+):[0-9]+:[0-9]+/;
const WIN_POSIX_DRIVE_REGEX = /^\/[A-Z]:\/*/;
const pathDirname = (path) => {
let dirname = DIRNAME_POSIX_REGEX.exec(path)?.[1];
if (!dirname) {
dirname = DIRNAME_WIN32_REGEX.exec(path)?.[1];
}
if (!dirname) {
throw new Error(`Can't extract dirname from ${path}`);
}
return dirname;
};
const getPathFromErrorStack = () => {
let path = '';
const stack = new Error().stack;
if (!stack) {
console.warn("Error has no stack!");
return path;
}
// Node.js
let initiator = stack.split('\n').slice(4, 5)[0];
// Other
if (!initiator) {
initiator = stack.split('\n').slice(3, 4)[0];
}
if (initiator) {
path = EXTRACT_PATH_REGEX.exec(initiator)?.groups?.path || '';
}
if (!initiator || !path) {
console.warn("Can't get path from error stack!");
}
return path;
};
const getPath = () => {
let path = getPathFromErrorStack();
// Remove protocol
const protocol = "file://";
if (path.indexOf(protocol) >= 0) {
path = path.slice(protocol.length);
}
// Transform to win32 path
if (WIN_POSIX_DRIVE_REGEX.test(path)) {
path = path.slice(1).replace(/\//g, '\\');
}
return path;
};
/**
* Cross platform implementation for `__dirname`.
*
* @note Please do not use this method in nested other methods,
* instead always use it in the root of your file, otherwise it may return wrong results.
* @returns What `__dirname` would return in CJS
*/
export const getDirname = () => {
let path = getPath();
const dirname = pathDirname(path);
return dirname;
};
/**
* Cross platform implementation for `__filename`.
*
* @note Please do not use this method in nested other methods,
* instead always use it in the root of your file, otherwise it may return wrong results.
* @returns What `__filename` would return in CJS
*/
export const getFilename = () => {
let filename = getPath();
return filename;
};
+16
View File
@@ -0,0 +1,16 @@
/**
* Cross platform implementation for `__dirname`.
*
* @note Please do not use this method in nested other methods,
* instead always use it in the root of your file, otherwise it may return wrong results.
* @returns What `__dirname` would return in CJS
*/
export declare const getDirname: () => string;
/**
* Cross platform implementation for `__filename`.
*
* @note Please do not use this method in nested other methods,
* instead always use it in the root of your file, otherwise it may return wrong results.
* @returns What `__filename` would return in CJS
*/
export declare const getFilename: () => string;
+55
View File
@@ -0,0 +1,55 @@
{
"name": "cross-dirname",
"version": "0.1.0",
"type": "commonjs",
"main": "./dist/cjs/index.cjs",
"module": "./dist/esm/index.mjs",
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"require": "./dist/cjs/index.cjs",
"default": "./dist/esm/index.mjs"
}
},
"files": [
"/dist"
],
"scripts": {
"clear": "rm -rf dist",
"build": "npm run clear && npm run build:cjs && npm run build:esm && npm run build:types",
"build:cjs": "tsc && mv ./dist/cjs/index.js ./dist/cjs/index.cjs",
"build:esm": "tsc --project tsconfig.esm.json && mv ./dist/esm/index.js ./dist/esm/index.mjs",
"build:types": "tsc --project tsconfig.types.json",
"test": "npm run test:node && npm run test:deno && npm run test:gjs && npm run test:browser",
"test:node": "mocha test/node",
"test:gjs": "node test/browser/esbuild.mjs && gjs -m ./test/gjs/base.test.mjs",
"test:deno": "deno test ./test/deno/base.test.mjs",
"test:browser": "npm run test:browser:esm && npm run test:browser:cjs",
"test:browser:esm": "node test/browser/esbuild.mjs && mocha-headless-chrome --timeout 120000 --polling 1000 -f ./test/browser/index.html",
"test:browser:cjs": "node test/browser/esbuild.cjs && mocha-headless-chrome --timeout 120000 --polling 1000 -f ./test/browser/index.html"
},
"keywords": [
"dirname",
"cross-platform",
"esm",
"cjs",
"node",
"deno",
"gjs"
],
"url": "https://github.com/JumpLink/cross-dirname",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/JumpLink/cross-dirname.git"
},
"devDependencies": {
"chai": "^4.3.6",
"cross-dirname": "^0.0.6",
"esbuild": "^0.14.49",
"esm": "^3.2.25",
"mocha": "^10.0.0",
"mocha-headless-chrome": "^4.0.0",
"typescript": "^4.7.4"
}
}