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

feat: support import.meta.dirname and import.meta.filename #14854

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- `[jest-runtime]` Support `import.meta.filename` and `import.meta.dirname` (available from [Node 20.11](https://nodejs.org/en/blog/release/v20.11.0))
- `[jest-circus, jest-cli, jest-config]` Add `waitNextEventLoopTurnForUnhandledRejectionEvents` flag to minimise performance impact of correct detection of unhandled promise rejections introduced in [#14315](https://github.com/jestjs/jest/pull/14315) ([#14681](https://github.com/jestjs/jest/pull/14681))
- `[jest-circus]` Add a `waitBeforeRetry` option to `jest.retryTimes` ([#14738](https://github.com/jestjs/jest/pull/14738))
- `[jest-circus, jest-jasmine2]` Allow `setupFilesAfterEnv` to export an async function ([#10962](https://github.com/jestjs/jest/issues/10962))
Expand Down
21 changes: 21 additions & 0 deletions e2e/native-esm/__tests__/native-esm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,34 @@ test('should have correct import.meta', () => {
expect(typeof require).toBe('undefined');
expect(typeof jest).toBe('undefined');
expect(import.meta).toEqual({
dirname: expect.any(String),
filename: expect.any(String),
jest: expect.anything(),
url: expect.any(String),
});
expect(import.meta.jest).toBe(jestObject);
expect(
import.meta.url.endsWith('/e2e/native-esm/__tests__/native-esm.test.js'),
).toBe(true);
if (process.platform === 'win32') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this correct? If it's a file:// thing, it should use forward slashes. See examples in https://nodejs.org/api/url.html#urlpathtofileurlpath

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have access to a windows PC, or this'd be easy to check 😅

@G-Rath would you be able to quickly check for us what import.meta.filename and import.meta.dirname returns on a windows machine?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing with an arbitrary file on v21 gives me:

// file.js
import { fileURLToPath } from 'node:url';
console.log(fileURLToPath(import.meta.url));
console.log(import.meta.filename);
console.log(import.meta.url);
C:\Users\G-Rath\workspace\projects-oss\jest\file.mjs
C:\Users\G-Rath\workspace\projects-oss\jest\file.mjs
file:///C:/Users/G-Rath/workspace/projects-oss/jest/file.mjs

Let me know if that's enough or if you'd like me to try actually running this test (or other arbitrary bits of code)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SimenB filename and dirname are OS specific, but meta.url has URL format with forward slashes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, thanks @G-Rath!

expect(
import.meta.filename.endsWith(
'\\e2e\\native-esm\\__tests__\\native-esm.test.js',
),
).toBe(true);
expect(import.meta.dirname.endsWith('\\e2e\\native-esm\\__tests__')).toBe(
true,
);
} else {
expect(
import.meta.filename.endsWith(
'/e2e/native-esm/__tests__/native-esm.test.js',
),
).toBe(true);
expect(import.meta.dirname.endsWith('/e2e/native-esm/__tests__')).toBe(
true,
);
}
});

test('should double stuff', () => {
Expand Down
23 changes: 19 additions & 4 deletions packages/jest-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,11 @@ export default class Runtime {
initializeImportMeta: (meta: JestImportMeta) => {
meta.url = pathToFileURL(modulePath).href;

// @ts-expect-error Jest uses @types/node@16. Will be fixed when updated to @types/[email protected]
meta.filename = fileURLToPath(meta.url);
Copy link
Member

@SimenB SimenB Jan 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we only add these if the underlying Node version supports them? That way the tests won't behave differently than at runtime if people rely on it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say it should not be necessary as users on node < 20.11 won't use those variables in code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SimenB Do you want to add the check? I would backport this change also to v29 when it is ready.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to add the check?

nah, I'm fine with it. If a feature check were easy it'd be fine, but it's a bit clunky, and I don't wanna check against specific node versions.

I would backport this change also to v29 when it is ready.

We don't do backports, so there's no need for that. Thanks for offering, though!

// @ts-expect-error Jest uses @types/node@16. Will be fixed when updated to @types/[email protected]
meta.dirname = path.dirname(meta.filename);

let jest = this.jestObjectCaches.get(modulePath);

if (!jest) {
Expand Down Expand Up @@ -672,6 +677,13 @@ export default class Runtime {
initializeImportMeta(meta: ImportMeta) {
// no `jest` here as it's not loaded in a file
meta.url = specifier;

if (meta.url.startsWith('file://')) {
// @ts-expect-error Jest uses @types/node@16. Will be fixed when updated to @types/[email protected]
meta.filename = fileURLToPath(meta.url);
// @ts-expect-error Jest uses @types/node@16. Will be fixed when updated to @types/[email protected]
meta.dirname = path.dirname(meta.filename);
}
},
});
}
Expand All @@ -685,19 +697,22 @@ export default class Runtime {
specifier = fileURLToPath(specifier);
}

const [path, query] = specifier.split('?');
const [specifierPath, query] = specifier.split('?');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: Conflicting name with imported node:path.


if (
await this._shouldMockModule(
referencingIdentifier,
path,
specifierPath,
this._explicitShouldMockModule,
)
) {
return this.importMock(referencingIdentifier, path, context);
return this.importMock(referencingIdentifier, specifierPath, context);
}

const resolved = await this._resolveModule(referencingIdentifier, path);
const resolved = await this._resolveModule(
referencingIdentifier,
specifierPath,
);

if (
// json files are modules when imported in modules
Expand Down
Loading