bcherny/json-schema-to-typescript

cwd option creates invalid path

McBen opened this issue · 12 comments

McBen commented

(on windows) looks like it adds the process path to the CWD option.
worked fine in 11.0.3

with 12.0 I got:

ResolverError: Error opening file "E:\myproject\node_modules\@bcherny\json-schema-ref-parser\E:\myproject\src\api\v2\schema\index.schema"
[...]
    at ReadFileContext.callback (E:\myproject\node_modules\@bcherny\json-schema-ref-parser\cjs\resolvers\file.js:96:32)
    at FSReqCallback.readFileAfterOpen [as oncomplete] (node:fs:314:13) {

my code:

const fs = require("fs");
const path = require("path");
const json2ts = require("json-schema-to-typescript");

const fileToCompile = "src/api/v2/schema/index.schema"
  json2ts.compileFromFile(fileToCompile, {
      unknownAny: false,
      cwd: path.join(process.cwd(), path.dirname(fileToCompile)), // <- process.cwd added to express the problem
      style: { tabWidth: 4 }
  })
      .then(ts => fs.writeFileSync(path.join(path.dirname(fileToCompile), "compiled.ts"), ts));
}

We're seeing this too, with or without the CWD option set, via the CLI

Affects 12.0.0, 11.0.5, 11.0.4 - 11.0.3 - 11.01 seem fine

tpina commented

👍🏼 on this

$~> json2ts --cwd "C:/Users/xyz/Desktop/schemas/quicktype_example/schemas" schemas/bridgeRequest.schema.json foo.ts

[
  {
    stack: 'ResolverError: Error opening file "C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\schemas\\appIdentifier.schema.json" \n' +
      "ENOENT: no such file or directory, open 'C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\schemas\\appIdentifier.schema.json'\n" +
      '    at ReadFileContext.callback (C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\cjs\\resolvers\\file.js:96:32)\n' +
      '    at FSReqCallback.readFileAfterOpen [as oncomplete] (node:fs:324:13)',
    code: 'ERESOLVER',
    message: 'Error opening file "C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\schemas\\appIdentifier.schema.json" \n' +
      "ENOENT: no such file or directory, open 'C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\schemas\\appIdentifier.schema.json'",
    source: 'C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\schemas\\appIdentifier.schema.json',
    path: null,
    toJSON: [Function: toJSON],
    ioErrorCode: 'ENOENT',
    name: 'ResolverError',
    footprint: 'null+C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\schemas\\appIdentifier.schema.json+ERESOLVER+Error opening file "C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\schemas\\appIdentifier.schema.json" \n' + 
      "ENOENT: no such file or directory, open 'C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\xyz\\Desktop\\schemas\\quicktype_example\\schemas\\appIdentifier.schema.json'",
    toString: [Function: toString]
  }
]

This is coming from the json-schema-ref-parser library.
Someone has reported it there as well: APIDevTools/json-schema-ref-parser#311

I put together a PR to fix it: APIDevTools/json-schema-ref-parser#315 not sure how quickly a maintainer will get to it.
This library technically uses a fork of that, so maybe can also pull over my PR into the fork.

same here:

node:internal/process/promises:288
            triggerUncaughtException(err, true /* fromPromise */);
            ^

ResolverError: Error opening file "C:\Users\55119\Elucidário.art\elucidario\node_modules\@bcherny\json-schema-ref-parser\C:\Users\55119\Elucidário.art\elucidario\packages\md-to-gdoc\src\presets\abnt\schemas\referencias\autor.json"
ENOENT: no such file or directory, open 'C:\Users\55119\Elucidário.art\elucidario\node_modules\@bcherny\json-schema-ref-parser\C:\Users\55119\Elucidário.art\elucidario\packages\md-to-gdoc\src\presets\abnt\schemas\referencias\autor.json'
    at ReadFileContext.callback (c:\Users\55119\Elucid%C3%A1rio.art\elucidario\node_modules\@bcherny\json-schema-ref-parser\cjs\resolvers\file.js:96:32)
    at FSReqCallback.readFileAfterOpen [as oncomplete] (node:fs:324:13) {
  code: 'ERESOLVER',
  source: 'C:\\Users\\55119\\Elucidário.art\\elucidario\\node_modules\\@bcherny\\json-schema-ref-parser\\C:\\Users\\55119\\Elucidário.art\\elucidario\\packages\\md-to-gdoc\\src\\presets\\abnt\\schemas\\referencias\\autor.json',
  path: null,
  toJSON: [Function: toJSON],
  ioErrorCode: 'ENOENT',
  [Symbol(nodejs.util.inspect.custom)]: [Function: inspect]
}

Node.js v18.12.1
meepen commented

My current workaround is providing this to options:

{
  $refOptions: {
    resolve: {
      file: {
        async read(file: FileInfo): Promise<string> {
          if (
            process.platform === 'win32' &&
            file.url.split(':').length > 1
          ) {
            [, file.url] = file.url.match(/(.:[^:]+)$/) ?? [, 'error'];
          }
  
          return await readFile(file.url, 'utf8');
        },
      },
    },
}

@matt1097 PRs for https://github.com/bcherny/json-schema-ref-parser are welcome. I also see a reasonable suggestion on your PR -- could using Node's path library simplify some of the cross-platform logic?

I'll make a PR.
I believe the reason that library does not use path is because it can work directly in the browser without node
I was just mimicking, existing code style.

@bcherny since you already use path in your library here, i am willing to switch that chunk of code over to using path. the tradeoff being your fork becoming more distant from the original. If you are ok with that, I will take a stab at it.

I'll make a PR. I believe the reason that library does not use path is because it can work directly in the browser without node I was just mimicking, existing code style.

@bcherny since you already use path in your library here, i am willing to switch that chunk of code over to using path. the tradeoff being your fork becoming more distant from the original. If you are ok with that, I will take a stab at it.

Why not use a library like pathe?
https://www.npmjs.com/package/pathe

we experience this issue too

Would love to see this resolved as while meepen's solution worked at first, having a schema reference a file in the same folder causes it to error again.

I'm using pnpm, nodejs 18, and package.json "type": "module". In summary, I'm in ESM mode with pnpm as the package manager. It works fine on the CI's Linux and my MacBook, but it doesn't work on my colleague's Windows laptop.

I tried to debug and found some issues in @apidevtools/json-schema-ref-parser/dist/lib/util.js:

  1. __dirname is not available in ESM mode (it's used to calculate projectPath, but __dirname is incorrect; it would be node_modules/.pnpm/@apidevtools/json-schema-ref-parser/dist/lib/util/url.js). From what I can see, it's not needed. I'm not sure about the browser environment, but I think browsers don't have node?
  2. Many checks for the Windows environment are unnecessary. If it's a node environment, using path.resolve or join would suffice.

My temporary solution is to use pnpm patch (or patch-package if not using pnpm). I modified @apidevtools/json-schema-ref-parser/dist/lib/util.js by removing the Windows-related code. This way, it runs on Windows without affecting Linux and Mac. You can also replace it with path.resolve.

For instance, I encountered an issue with this function:

const path_1 = require("path");

function fromFileSystemPath(path) {
    // Step 1: On Windows, replace backslashes with forward slashes,
    // rather than encoding them as "%5C"
    if (isWindows) {
        const hasProjectDir = path.toUpperCase().includes(projectDir.replace(/\\/g, "\\").toUpperCase());
        const hasProjectUri = path.toUpperCase().includes(projectDir.replace(/\\/g, "/").toUpperCase());
        if (hasProjectDir || hasProjectUri) {
            path = path.replace(/\\/g, "/");
        }
        else {
            path = `${projectDir}/${path}`.replace(/\\/g, "/");
        }
    }
    // Step 2: `encodeURI` will take care of MOST characters
    path = encodeURI(path);
    // Step 3: Manually encode characters that are not encoded by `encodeURI`.
    // This includes characters such as "#" and "?", which have special meaning in URLs,
    // but are just normal characters in a filesystem path.
    for (let i = 0; i < urlEncodePatterns.length; i += 2) {
        path = path.replace(urlEncodePatterns[i], urlEncodePatterns[i + 1]);
    }
    return path;
}
exports.fromFileSystemPath = fromFileSystemPath;

I modified it to:

const path_1 = require("path");

function fromFileSystemPath(path) {
    return path_1.resolve(path);
}
exports.fromFileSystemPath = fromFileSystemPath;

To apply the patch using pnpm:

pnpm patch  @apidevtools/json-schema-ref-parser
// Open the temporary folder and apply the above modifications
pnpm patch-commit tempfile  // Refer to pnpm's instructions

@bcherny Is it possible this issue will be fixed if https://github.com/bcherny/json-schema-ref-parser is synced with the latest from https://github.com/APIDevTools/json-schema-ref-parser, currently shows that its 47 commits behind. I see they have since merged in the fixes for Issue #311 in APIDevTools/json-schema-ref-parser#321.