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

basePath does not work if absolute, or if relative to a current working directory that is a sibling of the target directory. #599

Open
broofa opened this issue Apr 30, 2024 · 2 comments

Comments

@broofa
Copy link

broofa commented Apr 30, 2024

Issue

When generating a schema programmatically, specifying an absolute basePath causes a "Error: type [target TS type] not found" error, presumably because TSJ is failing to find the specified source file(s).

Similarly, if the basePath is relative to a current working directory that is off to the side of the target directory (i.e. cwd is not a parent of the target directory so basePath has ".." paths in it), it fails similarly.

Impact: This is a non-trivial issue because, combined, these two problems make it next to impossible to run multiple async schema generation tasks. The inability to provide an absolute path means basePath must be specified relative to process.cwd(). But relative paths only work if they descend directly from process.cwd(), which means you very likely need to change the working directory for things to work. (But that will likely break any other TSJ task that happens to depend on the current cwd).

To Reproduce

  1. Download and unzip
    tsj-test.zip.
    This is a minimal, reproducible example of the problem.
  2. cd tsj-test
  3. npm install
  4. npm test (with node@18 or node@20)

You should see output something like this:

▶ basePath tests
  ✔ {"cwd":"appdir","basePath":".","rootNames":["src/app.ts"]} (456.918416ms)
  ✔ {"cwd":".","basePath":".","rootNames":["appdir/src/app.ts"]} (273.435166ms)
  ✖ {"cwd":".","basePath":"/Users/kieffer/tsj-test/appdir","rootNames":["src/app.ts"]} (249.84875ms)
    AssertionError [ERR_ASSERTION]: Got unwanted rejection.
    Actual message: "type TargetType not found"
        at async TestContext.<anonymous> (file:///Users/kieffer/tsj-test/test.js:56:7)
        at async Test.run (node:internal/test_runner/test:632:9)
        at async Suite.processPendingSubtests (node:internal/test_runner/test:374:7) {
      generatedMessage: false,
      code: 'ERR_ASSERTION',
      actual: Error: type TargetType not found
          at JsonSchemaGenerator.getSchemaForSymbol (/Users/kieffer/tsj-test/node_modules/typescript-json-schema/dist/typescript-json-schema.js:1171:19)
          at Module.generateSchema (/Users/kieffer/tsj-test/node_modules/typescript-json-schema/dist/typescript-json-schema.js:1369:26)
          at generateSchemaFromType (file:///Users/kieffer/tsj-test/test.js:30:14)
          at file:///Users/kieffer/tsj-test/test.js:57:30
          at waitForActual (node:assert:777:21)
          at Function.doesNotReject (node:assert:932:39)
          at TestContext.<anonymous> (file:///Users/kieffer/tsj-test/test.js:56:20)
          at Test.runInAsyncScope (node:async_hooks:203:9)
          at Test.run (node:internal/test_runner/test:631:25)
          at Suite.processPendingSubtests (node:internal/test_runner/test:374:18),
      expected: undefined,
      operator: 'doesNotReject'
    }

  ✖ {"cwd":"./stubdir","basePath":"../otherdir","rootNames":["src/app.ts"]} (237.169625ms)
    AssertionError [ERR_ASSERTION]: Got unwanted rejection.
    Actual message: "type TargetType not found"
        at async TestContext.<anonymous> (file:///Users/kieffer/tsj-test/test.js:56:7)
        at async Test.run (node:internal/test_runner/test:632:9)
        at async Suite.processPendingSubtests (node:internal/test_runner/test:374:7) {
      generatedMessage: false,
      code: 'ERR_ASSERTION',
      actual: Error: type TargetType not found
          at JsonSchemaGenerator.getSchemaForSymbol (/Users/kieffer/tsj-test/node_modules/typescript-json-schema/dist/typescript-json-schema.js:1171:19)
          at Module.generateSchema (/Users/kieffer/tsj-test/node_modules/typescript-json-schema/dist/typescript-json-schema.js:1369:26)
          at generateSchemaFromType (file:///Users/kieffer/tsj-test/test.js:30:14)
          at file:///Users/kieffer/tsj-test/test.js:57:30
          at waitForActual (node:assert:777:21)
          at Function.doesNotReject (node:assert:932:39)
          at TestContext.<anonymous> (file:///Users/kieffer/tsj-test/test.js:56:20)
          at Test.runInAsyncScope (node:async_hooks:203:9)
          at Test.run (node:internal/test_runner/test:631:25)
          at Suite.processPendingSubtests (node:internal/test_runner/test:374:18),
      expected: undefined,
      operator: 'doesNotReject'
    }

The test attempts to generate schemas for the exact same code, using four different configurations of current working directory, basePath, and rootNames. The first two - which use relative basePath values with a current working directory in or above the target directory - work fine.

The third test, which uses an absolute basePath that points to the same directory as the second test, fails.

Similarly, the fourth test uses a relative path that points to the same directory, but with a current working directory "off to the side" (in ./stubdir) of the target directory, also fails.

Expected behavior

All four tests should pass.

@broofa broofa changed the title Absolute basePath does not work. basePath does not work if absolute, or if relative to a current working directory that is a sibling of the target directory. Apr 30, 2024
@broofa
Copy link
Author

broofa commented May 1, 2024

Quick followup to confirm that this issue does, indeed, cause TJS.generateSchema() operations to fail when run concurrently (async) on multiple projects. 😢

@broofa
Copy link
Author

broofa commented May 3, 2024

[Breadcrumb for anyone else that might stumble across this issue...]

This appears to be an issue with the TS compiler API. Specifically, in ts.createProgram(). It looks like repeated calls to this method reuse the cached file information for any rootNames paths that match previous rootNames, regardless of basePath.

FWIW, you can diagnose this problem by just doing console.log(program) and looking at the contents of the program.sourceFileToPackageName map. If it's showing unexpected files... this is probably the issue.

The solution ("workaround"? "Hack"?) is to use a basePath in getProgramFiles() that is "high enough" to encompass all of the rootNames paths to be processed, so each rootNames path is unique across all sources being processed.

Maintainers: I'm going to leave this open since it affects the expected use of TJS, and in the hopes someone more knowledgable than I might know a better workaround. But if not, feel free to close.

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

1 participant