Previously our means of supporting ESM configuration files, formatters and snippet syntaxes was just to attempt a require() and then fall back to await import() in a catch block if it failed. This was a temporary measure, which this PR replaces with something more permanent.
Due the different structures of ESM vs CommonJS when imported, this has a little complexity. For formatters and snippet syntaxes, we can switch to await import() and just walk a couple of levels down the object looking for a default key that is a function.
For config, the logic above would be too fuzzy (profile keys of "default" etc), so we use await import() or require() as appropriate based on the file extension. For .js files we check the nearest package.json for the type and take that hint on whether to use require() or await import(). Also, throws if the extension isn't one of our supported ones, and adds some debug logging.
Also, we've been able to remove the hacky importer.js mechanism, which was added to get around TypeScript's behaviour (at the time) of transpiling all await import() statements down to Promise-wrapped require()s - the module mode node16 now solves this.
This will be a breaking change for any users who were relying on our internal usage of require() to dynamically transpile configuration files, formatters or snippet syntaxes (i.e. by registering a transpiler first). The suggested change will be to transpile beforehand and load the output file.
Configuration files are loaded with the appropriate mechanism based on file/package type
Custom formatters/snippets are only loaded with await import()
🏷️ What kind of change is this?
:bank: Refactoring/debt/DX (improvement to code design, tooling, documentation etc. without changing behaviour)
:boom: Breaking change (incompatible changes to the API)
coverage: 97.872% (-0.08%) from 97.952% when pulling d58e57569c329f663364a49388afef83b9ecee23 on feat/always-import-config into eb1890f4cd7a6b850c86e7f60b20f928565804d0 on main.
🤔 What's changed?
Previously our means of supporting ESM configuration files, formatters and snippet syntaxes was just to attempt a
require()
and then fall back toawait import()
in a catch block if it failed. This was a temporary measure, which this PR replaces with something more permanent.Due the different structures of ESM vs CommonJS when imported, this has a little complexity. For formatters and snippet syntaxes, we can switch to
await import()
and just walk a couple of levels down the object looking for adefault
key that is a function.For config, the logic above would be too fuzzy (profile keys of "default" etc), so we use
await import()
orrequire()
as appropriate based on the file extension. For.js
files we check the nearestpackage.json
for the type and take that hint on whether to userequire()
orawait import()
. Also, throws if the extension isn't one of our supported ones, and adds some debug logging.Also, we've been able to remove the hacky
importer.js
mechanism, which was added to get around TypeScript's behaviour (at the time) of transpiling allawait import()
statements down to Promise-wrappedrequire()
s - the module modenode16
now solves this.This will be a breaking change for any users who were relying on our internal usage of
require()
to dynamically transpile configuration files, formatters or snippet syntaxes (i.e. by registering a transpiler first). The suggested change will be to transpile beforehand and load the output file.⚡️ What's your motivation?
https://github.com/cucumber/cucumber-js/discussions/2059:
🏷️ What kind of change is this?
📋 Checklist:
This text was originally generated from a template, then edited by hand. You can modify the template here.