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

Cannot use import.meta.url in _.context.ts #1086

Open
jrunning opened this issue Oct 17, 2024 · 7 comments
Open

Cannot use import.meta.url in _.context.ts #1086

jrunning opened this issue Oct 17, 2024 · 7 comments
Labels
enhancement New feature or request

Comments

@jrunning
Copy link

jrunning commented Oct 17, 2024

I wanted to load the contents of a JSON file so I put some code like this in my /api/routes/_.context.ts file, as I'm accustomed to in ES modules:

import {createRequire} from 'module';

const require = createRequire(import.meta.url);
const mockResponse = require('../mocks/response.json');

export class Context {
  getResponse() {
    return mockResponse;
  }
}

When I run Counterfact with the usual arguments I get the following error after "Starting REPL, type .help for more info":

Starting REPL, type .help for more info

node:internal/process/promises:389
      new UnhandledPromiseRejection(reason);
      ^

UnhandledPromiseRejection: This error originated either by throwing inside of an async function 
without a catch block, or by rejecting a promise which was not handled with .catch(). The promise 
rejected with the reason "Error: Line 15: Unexpected reserved word".
    at throwUnhandledRejectionsMode (node:internal/process/promises:389:7)
    at processPromiseRejections (node:internal/process/promises:470:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:96:32) {
  code: 'ERR_UNHANDLED_REJECTION'
}

Node.js v22.3.0

The "unexpected reserved word" in question is the import in import.meta.url, which I have confirmed by removing all code except for console.log(import.meta).

However, when I run the _.context.ts file directly (with tsx and adding console.log(mockResponse) at the end), it runs without an error:

$ npx tsx api/routes/_.context.ts
{ hello: 'world' }

I think something is going wrong in Counterfact's module loader, but I'm not sure what.

In the meantime I'm using JSON.parse(fs.readFileSync(...)), which works fine, but is less than optimal, and there may be other uses of import.meta that are blocked by this issue.

Minimal reproduction

I've created a minimal reproduction of the issue here: jrunning/counterfact-repro.

@dethell
Copy link
Collaborator

dethell commented Oct 17, 2024

I've played with the repo and am trying some alternate ways to load the file. This gets closer:

import { readFileSync } from "fs";
let data;
try {
  const fileData = readFileSync("../../test.json", { encoding: "utf-8" });
  console.log("fileData", fileData);
  data = JSON.parse(readFileSync("../../test.json", { encoding: "utf-8" }));
} catch (error) {
  console.error("parse error!", error);
  data = {};
}

But that doesn't seem to find the proper JSON file so I'm still doing something wrong. But something like this might be an alternative to using require.

@dethell
Copy link
Collaborator

dethell commented Oct 17, 2024

Ok, using an absolute path works. Probably a better way to help the compiled code in .cache find the desired json file.

@jrunning
Copy link
Author

I've discovered that require is already in scope in _.context.ts (even though it's an ES module—confusing) and while require('../../test.json') doesn't work (it looks for ../../test.json.cjs), require.resolve('../../test.json') does work, so I can do the following:

JSON.parse(readFileSync(require.resolve('../../test.json'), 'utf8'));

@dethell
Copy link
Collaborator

dethell commented Oct 17, 2024

I've discovered that require is already in scope in _.context.ts (even though it's an ES module—confusing) and while require('../../test.json') doesn't work (it looks for ../../test.json.cjs), require.resolve('../../test.json') does work, so I can do the following:

JSON.parse(readFileSync(require.resolve('../../test.json'), 'utf8'));

I guess since all the code transpiles down to cjs this is why require is available. Glad this use case works.

@pmcelhaney
Copy link
Owner

pmcelhaney commented Oct 17, 2024

Nice solution, @jrunning!

I guess since all the code transpiles down to cjs this is why require is available.

Yep! Because there's no API to clear the module cache with ESM, and I wanted to be able to "hot reload" code, I had to compile everything down to CJS. It gets the job done but leaves us with some leaky abstractions like this one.

Would it help if we add a utility function to Counterfact for loading JSON files?

export class Context {
  constructor({ readJson }) {
     this.readJson = readJson;
  }

  getResponse() {
    return this.readJson('../mocks/response.json');
  }
}

@jrunning
Copy link
Author

Would it help if we add a utility function to Counterfact for loading JSON files?

It seems like loading data from JSON could be a common use case, but the only data point I have is my own.

@pmcelhaney
Copy link
Owner

I'm sure it's common enough, just gut-checking the API.

@pmcelhaney pmcelhaney added the enhancement New feature or request label Jan 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants