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

Test coverage: moduleType overrides during ts-node loader usage #1376

Merged
merged 15 commits into from Jul 9, 2021
Merged

Test coverage: moduleType overrides during ts-node loader usage #1376

merged 15 commits into from Jul 9, 2021

Conversation

jayaddison
Copy link
Contributor

Extends #1371.

This isn't working as expected currently: when running the test.esm.js module directly (from the base directory: TS_NODE_PROJECT=tests/module-types/tsconfig.json node --trace-warnings --experimental-specifier-resolution=node --loader ts-node/esm tests/module-types/test.esm.js), one of the assertions fails because both imported modules have a value of function for requireType.

Without the --experimental-specifier-resolution=node flag, a ERR_UNSUPPORTED_DIR_IMPORT error is raised by the attempts to import modules from directory paths.

@cspotcode
Copy link
Collaborator

Thanks, what needs to happen next? Does tests/module-types/tsconfig.json need to be modified to add the necessary rule for esm-exception?

Without the --experimental-specifier-resolution=node flag, a ERR_UNSUPPORTED_DIR_IMPORT error is raised by the attempts to import modules from directory paths.

Is this necessary to get code coverage?

@codecov
Copy link

codecov bot commented Jun 23, 2021

Codecov Report

Merging #1376 (c0a954f) into ab/override-module-type (dcbd916) will increase coverage by 0.32%.
The diff coverage is n/a.

Impacted Files Coverage Δ
src/esm.ts 87.50% <0.00%> (+7.14%) ⬆️

@jayaddison
Copy link
Contributor Author

Does tests/module-types/tsconfig.json need to be modified to add the necessary rule for esm-exception?

I don't think so - that tsconfig.json should already set an ESM override for files in subdirectores, if I understand it correctly?

(just in case, I did try adding an explicit esm-exceptions subdirectory configuration there; no dice, both requireType values are still returned as function)

Without the --experimental-specifier-resolution=node flag, a ERR_UNSUPPORTED_DIR_IMPORT error is raised by the attempts to import modules from directory paths.

Is this necessary to get code coverage?

Not for code coverage, no, but to get the test module to run without raising that error it does seem to be required.

@cspotcode
Copy link
Collaborator

cspotcode commented Jun 23, 2021

I don't think so - that tsconfig.json should already set an ESM override for files in subdirectores, if I understand it correctly?

Oh, I see the confusion. The logic shares elements of both TS "include" globbing and .gitignore / .gitattributes.

Globs match the behavior of TS "include", so src/cjs-subdir matches everything in that directory. It is an "implicit glob."

Patterns are matched from top to bottom, and anything at the bottom overrides anything at the top. Same as .gitignore or .gitattributes where anything at the bottom overrides anything at the top.

// Test that subsequent patterns override earlier ones
"src/cjs-subdir/**/*": "esm",
"src/cjs-subdir": "cjs"

Not for code coverage, no, but to get the test module to run without raising that error it does seem to be required.

What about importing the files by name instead of importing the directory?

@jayaddison
Copy link
Contributor Author

Thank you - I've applied some rearrangements here, and am now able to run TS_NODE_PROJECT=tests/module-types/tsconfig.json node --trace-warnings --experimental-specifier-resolution=node --loader ts-node/esm tests/module-types/test.esm.js and get a passing result.

That said I'll want to reconfirm that the test coverage is really proving what it claims to, and also ensure that it works when run as part of the standard test suite.

@jayaddison
Copy link
Contributor Author

(to try to explain what I meant about proving the test coverage: we should get confidence that the overrides are really being used, to make sure that passing test results aren't simply a result of default behaviour)

@cspotcode
Copy link
Collaborator

to make sure that passing test results aren't simply a result of default behaviour

Agreed. Does it make sense to copy-paste tests/module-types into tests/module-types-2, and make it into an opposite of the first project? That is to say, one of them has package.json type: "module" and uses overrides to force CommonJS, and the other project has package.json without type and uses overrides to force ESM.

Can we remove --experimental-specifier-resolution=node? That would make the tests a bit simpler to understand.

@jayaddison
Copy link
Contributor Author

jayaddison commented Jun 23, 2021

to make sure that passing test results aren't simply a result of default behaviour

Agreed. Does it make sense to copy-paste tests/module-types into tests/module-types-2, and make it into an opposite of the first project? That is to say, one of them has package.json type: "module" and uses overrides to force CommonJS, and the other project has package.json without type and uses overrides to force ESM.

Sounds good; I've been messing around a bit further here as you'll see, but the tests do not pass yet.

Can we remove --experimental-specifier-resolution=node? That would make the tests a bit simpler to understand.

I'll try to, yep. Whenever I've tried that previously it has led to the ERR_UNSUPPORTED_DIR_IMPORT errors, but I'll try to learn more about that and remove the additional option if possible.

(I now understand that this should be addressed by the direct filename imports you mentioned previously; I'll try that out soon)

@cspotcode
Copy link
Collaborator

cspotcode commented Jun 25, 2021

Test are failing on 14.13.0 and 12.15, but I think I know why. I believe we test on these versions specifically because they are old enough that they do not throw an error when attempting to require() ESM; instead they allow it to be executed as CJS. We'll need to figure out what the correct behavior is in those situations. I assume we should either tweak ts-node to throw the error, respecting the moduleTypes overrides, or disable these unit tests on those versions, because users can reasonably be expected to upgrade to the latest node 12 or 14 to leverage newer ts-node features.

@jayaddison
Copy link
Contributor Author

Thanks @cspotcode - I guess that kind of require of ESM-as-CJS-by-default might only work by chance, if at all?

Looking at some of the code comments (particularly this one), would it be fair to say that ts-node should try to do what node itself does?

(in this case currently, that's to throw an ERR_UNKNOWN_FILE_EXTENSION type error exception under Node 12 due to the .ts extension..)

@jayaddison
Copy link
Contributor Author

(in this case currently, that's to throw an ERR_UNKNOWN_FILE_EXTENSION type error exception under Node 12 due to the .ts extension..)

...I've since guessed that that's probably not a sensible error to reproduce, because that was encountered when running the test.esm.js code without any preprocessing / transpilation (if that's the correct term?) of the imported .ts files into JavaScript.

@cspotcode
Copy link
Collaborator

cspotcode commented Jun 26, 2021

The goal is to match node's behavior. The behavior matching is as follows:

  • .ts, .tsx, .js, .jsx files: mimic node's handling of .js files, as if the file had the .js extension
  • .ts, .tsx, .jsx that match a moduleTypes override:
    • if the override is CJS: behave as if the file had the .cjs extension. Whatever node would do for a .cjs file, we do the same
    • if the override is ESM: behave as if the file had the .mjs extension. Whatever node would do for a .mjs file, we do the same

We must behave the same way that node would, which means:

  • if node would throw an error when attempting to require the file, we should throw the same error
  • if node would execute the file as CJS, we should do the same
  • if node would execute the file as ESM, we should do the same

In order to correctly match node's behavior with the ERR_REQUIRE_ESM error, which has changed over time, we check node's version number:
https://github.com/TypeStrong/ts-node/blob/main/src/index.ts#L33-L38
(we also version switch in one other place, but that is for a different reason)

@cspotcode
Copy link
Collaborator

I just realized your comment was about ERR_UNKNOWN_FILE_EXTENSION, not about ERR_REQUIRE_ESM. My mistake. I've edited my last comment to say ERR_REQUIRE_ESM since that is what I was referring to.

The goal regarding ERR_UNKNOWN_FILE_EXTENSION is to teach node what to do for .jsx, .ts, and .tsx extensions so that it doesn't have to throw that error when it encounters those files. I agree with you that it is not a sensible error to reproduce, because we want to teach node to avoid that error.

@jayaddison
Copy link
Contributor Author

No problem at all; we're on the same page, I think - and it's better to overcommunicate in these kinds of conversation.

If I understand correctly then we'll end up with a test matrix of six possible outcomes when moduleTypes overrides are in place:

  • Two outcomes like the ones that pass already for recent Node versions (1x CJS, 1x ESM)
  • Two outcomes for Node ~= 14.13 (1x CJS, 1x ESM)
  • Two outcomes for Node ~= 12.15 (1x CJS, 1x ESM)

It's possible these collapse down to four outcomes, but it'll be a little while before I have time to confirm behaviour across both Node 12.15 and Node 14.13.

I also get the feeling that the test coverage here might end up being a base level of coverage rather than exhaustive; in other words, it's not going to reliably test all the differences that could occur for varying CJS and EJS file content. Still, making sure that each line of code is exercised seems like a worthwhile improvement.

Feel free to continue on further with the branch if you will have more time and/or want to progress this soon. I should have some time outside work hours next week to look at this again.

@jayaddison
Copy link
Contributor Author

I've added 'expected failure' cases for the moduleType override test under Node 12.15, 14.13 - but also have to admit I haven't yet gone upstream to Node to figure out if those are really the ranges affected (i.e. why the failures on those versions as of 7b6daa3 occurred for those unit test runs).

@cspotcode
Copy link
Collaborator

cspotcode commented Jul 7, 2021 via email

@jayaddison jayaddison marked this pull request as ready for review July 7, 2021 18:13
@jayaddison
Copy link
Contributor Author

Is there anything else required here?

I don't think so? Although I do feel a bit out of my depth here.

@cspotcode
Copy link
Collaborator

@jayaddison ok, thanks, I'll give this a proper review as soon as I have time, and hopefully get this merged.

@jayaddison
Copy link
Contributor Author

Thanks in advance @cspotcode, and take your time; glad to make any further adjustments to make sure the code's acceptable.

@cspotcode cspotcode merged commit e0f460e into TypeStrong:ab/override-module-type Jul 9, 2021
@jayaddison jayaddison deleted the ab/override-module-type branch July 9, 2021 07:42
cspotcode added a commit that referenced this pull request Jul 9, 2021
* Add moduleType option to override module type on certain files.  Also
refactor resolverFunctions into their own file; should break this into
2x PRs later.

* lint fix

* add test

* remove unnecessary exports from ts-internals; mark exports as @internal

* add docs

* strip optionBasePaths from showConfig output

* proper path normalization to support windows

* lint-fix

* use es2015 instead of es2020 in test tsconfig to support ts2.7

* add missing path normalization when calling classifyModule

* Test coverage: moduleType overrides during ts-node loader usage (#1376)

* Test coverage: add test case to confirm that moduleType overrides are applied for ts-node in loader mode

* Ensure that a default export exists for the esm-exception module

* Re-order tsconfig.json glob rules, and use implicit globbing

* lint fixup: apply prettier

* Add 'experimental-specifier-resolution' flag to NPM options in ESM module override test

* Ensure that a default export exists for the cjs-subdir module

* Revert "Ensure that a default export exists for the cjs-subdir module"

This reverts commit c64cf92.

* Revert "Add 'experimental-specifier-resolution' flag to NPM options in ESM module override test"

This reverts commit 1093df8.

* Specify tsconfig project using TS_NODE_PROJECT environment variable

* Use full file paths in preference to directory-default module imports

This avoids ERR_UNSUPPORTED_DIR_IMPORT, without having to provide the 'experimental-specifier-resolution' flag to ts-node

* Update index.ts

* Update index.ts

* Update tsconfig.json

* Extract execModuleTypeOverride function

* Add expected failure cases for Node 12.15, 14.13

Co-authored-by: Andrew Bradley <cspotcode@gmail.com>

* Update tests

* fix

* fix

* fix for TS2.7

* fix

* fix

* reword

* update docs

* address todos

* fix

Co-authored-by: James Addison <jay@jp-hosting.net>
@cspotcode cspotcode added this to the 10.x.x milestone Jul 9, 2021
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

Successfully merging this pull request may close these issues.

None yet

2 participants