Description
Currently, even if a user or developer is paying close attention to their browser's Console, they'd have a hard time acting upon a deprecation warning, unless they are operating within a very constrained environment (i.e. a single plugin and an assumption that Gutenberg itself would never violate any deprecations).
Instead, we should consider to make the messaging of a deprecation more actionable, specifically by naming the culprit and/or file from which the deprecation was caused. This way, a user could report the issue to the plugin author, or the plugin author could be more assured in observation that it's their code which requires change.
Task: Include the name of the plugin and/or file from the deprecation messaging.
Example:
Before:
wp.editor.withColors is deprecated and will be removed. Please use wp.blockEditor.withColors instead.
After:
wp.editor.withColors is deprecated and will be removed. Please use wp.blockEditor.withColors instead. This warning was caused by the Jetpack plugin (_inc/blocks/editor.js:13).
Implementation Notes:
The primary challenge is that in JavaScript, to the extent of my knowledge, we can't easily traverse up the caller stack to know from where the deprecated
call was triggered.
There are a few things to work with:
Function#caller
- Non-standard, forbidden in strict mode, likely not sufficient detail
console.trace
- Logs directly to console, can't be captured
Error#stack
- Non-standard, only accessible from context of a
catch
- Non-standard, only accessible from context of a
As I see it, I think the most viable option would be to explore Error#stack
as a progressive enhancement when available, using a faked thrown error. The output is a stack string, and it varies by browser, so it would need to be parsed in some form.
Example (Chrome):
Error
at Object.deprecated (http://editor.test/wp-content/plugins/gutenberg/build/deprecated/index.js?ver=1564598972:135:11)
at new PostTitle (http://editor.test/wp-content/plugins/gutenberg/build/editor/index.js?ver=1564598956:10213:8)
at constructClassInstance (http://editor.test/wp-content/plugins/gutenberg/vendor/react-dom.165d5c53.js:11446:7)
at updateClassComponent (http://editor.test/wp-content/plugins/gutenberg/vendor/react-dom.165d5c53.js:14776:5)
at beginWork (http://editor.test/wp-content/plugins/gutenberg/vendor/react-dom.165d5c53.js:15733:16)
at performUnitOfWork (http://editor.test/wp-content/plugins/gutenberg/vendor/react-dom.165d5c53.js:19401:12)
at workLoop (http://editor.test/wp-content/plugins/gutenberg/vendor/react-dom.165d5c53.js:19441:24)
at renderRoot (http://editor.test/wp-content/plugins/gutenberg/vendor/react-dom.165d5c53.js:19524:7)
at performWorkOnRoot (http://editor.test/wp-content/plugins/gutenberg/vendor/react-dom.165d5c53.js:20431:7)
at performWork (http://editor.test/wp-content/plugins/gutenberg/vendor/react-dom.165d5c53.js:20343:7)
Sample pseudo-code:
try {
throw new Error;
} catch ( error ) {
if ( typeof error.stack !== 'string' ) {
return;
}
const file = error.stack.split( '\n' )[ 2 ].match( /\(([^)]+)\)$/ )[ 1 ];
// "http://editor.test/wp-content/plugins/gutenberg/build/editor/index.js?ver=1564598956:10213:8"
}
If needed to back-reference the plugin, we could include a mapping of registered scripts from the server-side, including an (inferred perhaps also via debug_backtrace
) origin plugin name from which the script was registered.
It would probably be much more complicated than this, since there might be multiple intermediaries between the plugin script and the Gutenberg code which calls deprecate
(usually on some condition). Potentially then, we'd need to parse files line-by-line from the stack to find the first which matches a plugin path.