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

Load custom formatter by relative path only if it begins with a dot #1413

Merged
merged 6 commits into from
Nov 26, 2020

Conversation

dawn-minion
Copy link
Member

@dawn-minion dawn-minion commented Aug 26, 2020

This commit makes it possible to load a custom formatter that is not relative to the current working directory, such as one installed
globally, or via Yarn PNP. This change makes it so we only load by a relative path if it begins with a dot, otherwise it will load
it as a regular non-relative require.

Fixes #1412

@davidjgoss
Copy link
Contributor

davidjgoss commented Oct 5, 2020

Would we consider making this more explicit, maybe borrowing webpack's tilda syntax:

require a node module

$ cucumber-js --format "~@my-scope/my-formatter-package"

Normal (current) behaviour:

$ cucumber-js --format "lib/formatters/local-custom-formatter.js"

(Let me know if I've missed why this wouldn't work!)

@charlierudolph
Copy link
Member

charlierudolph commented Oct 10, 2020

@davidjgoss I don't think this is about a node module vs not. Global node modules cannot be required regularly and local node modules should be fine with being referenced relative to the current path.

@dawn-minion I don't understand why this is needed. The current implementation should work fine with absolute paths - which is what I assume you are using for globally installed modules. I'll comment on the issue as I want more specifics about the current problem you're experiencing

@charlierudolph charlierudolph changed the title #1412 Try loading formatter by relative dir then by name Try loading formatter by relative dir then by name Oct 10, 2020
@charlierudolph
Copy link
Member

Okay plug in play is very interesting. Let's discuss this more in the issue.

@dawn-minion dawn-minion changed the title Try loading formatter by relative dir then by name Load custom formatter by relative path only if it begins with a dot Oct 28, 2020
@dawn-minion
Copy link
Member Author

@charlierudolph Sorry for the delay, been busy with other things. I believe that should be it here.

@charlierudolph
Copy link
Member

Code changes look good. Can you please add a note to the changelog under breaking changes?

This commit makes it possible to load a custom formatter that is
not relative to the current working directory, such as one installed
globally, or via Yarn PNP. If the path begins with a . it will be
loaded relative to the current directory.
@dawn-minion
Copy link
Member Author

@charlierudolph Should be ready now - had to remove the relative path parser from world.ts as well, as it would strip the leading ./ from the tests. All tests should be passing now

const fullCustomFormatterPath = path.resolve(cwd, customFormatterPath)
CustomFormatter = require(fullCustomFormatterPath) // eslint-disable-line @typescript-eslint/no-var-requires
} else {
CustomFormatter = require(customFormatterPath) // eslint-disable-line @typescript-eslint/no-var-requires
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be using createRequire to run the require from the cwd

Copy link
Contributor

@davidjgoss davidjgoss Nov 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that seems a fair point, although per the docs:

module.createRequire(filename)#

Added in: v12.2.0

Given we're still supporting Node 10 for the time being (its EOL is April 2021), could use https://github.com/sindresorhus/import-cwd instead.

Copy link

@merceyz merceyz Nov 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest using https://yarnpkg.com/package/create-require as it will use the node builtins when it can, so createRequire on node >= 12.2 createRequireFromPath on node >= 10.12, then a polyfil for the rest which is much better

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should actually use it for both branches here, which simplifies the logic and has the same end result

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the above, works great for both cases, thanks for pointing it out

@davidjgoss davidjgoss mentioned this pull request Nov 24, 2020
5 tasks
@davidjgoss davidjgoss added this to the 7.0.0 milestone Nov 24, 2020
Copy link
Contributor

@davidjgoss davidjgoss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, a couple of suggestions.

const fullCustomFormatterPath = path.resolve(cwd, customFormatterPath)
CustomFormatter = require(fullCustomFormatterPath) // eslint-disable-line @typescript-eslint/no-var-requires
} else {
CustomFormatter = require(customFormatterPath) // eslint-disable-line @typescript-eslint/no-var-requires
Copy link
Contributor

@davidjgoss davidjgoss Nov 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that seems a fair point, although per the docs:

module.createRequire(filename)#

Added in: v12.2.0

Given we're still supporting Node 10 for the time being (its EOL is April 2021), could use https://github.com/sindresorhus/import-cwd instead.

CHANGELOG.md Outdated Show resolved Hide resolved
Comment on lines 112 to 118
if (customFormatterPath.startsWith('.')) {
const fullCustomFormatterPath = path.resolve(cwd, customFormatterPath)
CustomFormatter = require(fullCustomFormatterPath) // eslint-disable-line @typescript-eslint/no-var-requires
} else {
CustomFormatter = require(customFormatterPath) // eslint-disable-line @typescript-eslint/no-var-requires
}

Copy link

@merceyz merceyz Nov 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding create-require to the dependencies and importing it simplifies this down to

Suggested change
if (customFormatterPath.startsWith('.')) {
const fullCustomFormatterPath = path.resolve(cwd, customFormatterPath)
CustomFormatter = require(fullCustomFormatterPath) // eslint-disable-line @typescript-eslint/no-var-requires
} else {
CustomFormatter = require(customFormatterPath) // eslint-disable-line @typescript-eslint/no-var-requires
}
CustomFormatter = createRequire(cwd)(customFormatterPath)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to this

@dawn-minion
Copy link
Member Author

Sorry for the delay here, been a bit busy with a move. I've added a note about Yarn PnP support to the changelog, added create-require here as @merceyz suggested which simplifies the code. Ready for review again.

Comment on lines 111 to 113
let CustomFormatter = null

CustomFormatter = createRequire(cwd)(customFormatterPath)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let CustomFormatter = null
CustomFormatter = createRequire(cwd)(customFormatterPath)
const CustomFormatter = createRequire(cwd)(customFormatterPath)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

CHANGELOG.md Outdated
@@ -56,6 +57,7 @@ If anything is missing from the migration guide, please submit an issue.
* Custom formatters will need to migrate
* `json` formatter is deprecated and will be removed in next major release. Custom formatters should migrate to use the `message` formatter, or the [standalone JSON formatter](https://github.com/cucumber/cucumber/tree/master/json-formatter) as a stopgap.
* Remove long-deprecated `typeName` from options object for `defineParameterType` in favour of `name`
* Custom formatters are no longer loaded relative to the current directory unless it begins with a dot (e.g. `--format=./relpath/to/formatter`).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: They are always loaded relative to the cwd now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the note being that it uses the regular require search path relative to cwd now, instead of always trying to load a file by path from the cwd

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully that's a bit clearer now.

tsconfig.json Outdated Show resolved Hide resolved
@dawn-minion
Copy link
Member Author

Can't seem to reproduce the test failure on appveyor, it's passing locally here with node v10.23.0

Copy link
Contributor

@davidjgoss davidjgoss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dawn-minion I think the CI failure is an (unrelated) unstable test per #1457.

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.

Custom formatters can only be loaded by relative path
4 participants