-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
feat: Start warning on each use of a deprecated API #21939
Changes from 13 commits
21969fa
4332d9f
663da9d
c6759ac
e4d9f12
7a8e0c8
6dea3f8
f4d7b93
a62e36b
4ba7120
42fb12a
6732f0b
6facb4f
92ea4ba
7ceda6d
226a212
a717727
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { runEcho as runEcho2 } from "http://localhost:4545/run/warn_on_deprecated_api/mod.ts"; | ||
|
||
const p = Deno.run({ | ||
cmd: [ | ||
Deno.execPath(), | ||
"eval", | ||
"console.log('hello world')", | ||
], | ||
}); | ||
await p.status(); | ||
p.close(); | ||
|
||
async function runEcho() { | ||
const p = Deno.run({ | ||
cmd: [ | ||
Deno.execPath(), | ||
"eval", | ||
"console.log('hello world')", | ||
], | ||
}); | ||
await p.status(); | ||
p.close(); | ||
} | ||
|
||
await runEcho(); | ||
await runEcho(); | ||
|
||
for (let i = 0; i < 10; i++) { | ||
await runEcho(); | ||
} | ||
|
||
await runEcho2(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
Download http://localhost:4545/run/warn_on_deprecated_api/mod.ts | ||
Warning | ||
├ Use of deprecated "Deno.run()" API. | ||
│ | ||
├ This API will be removed in Deno 2.0. Make sure to upgrade to a stable API before then. | ||
│ | ||
├ Suggestion: Use "Deno.Command()" API instead. | ||
│ | ||
└ Stack trace: | ||
└─ at [WILDCARD]warn_on_deprecated_api/main.js:3:16 | ||
|
||
hello world | ||
Warning | ||
├ Use of deprecated "Deno.run()" API. | ||
│ | ||
├ This API will be removed in Deno 2.0. Make sure to upgrade to a stable API before then. | ||
│ | ||
├ Suggestion: Use "Deno.Command()" API instead. | ||
│ | ||
└ Stack trace: | ||
├─ at runEcho ([WILDCARD]warn_on_deprecated_api/main.js:14:18) | ||
└─ at [WILDCARD]warn_on_deprecated_api/main.js:25:7 | ||
|
||
hello world | ||
Warning | ||
├ Use of deprecated "Deno.run()" API. | ||
│ | ||
├ This API will be removed in Deno 2.0. Make sure to upgrade to a stable API before then. | ||
│ | ||
├ Suggestion: Use "Deno.Command()" API instead. | ||
│ | ||
└ Stack trace: | ||
├─ at runEcho ([WILDCARD]warn_on_deprecated_api/main.js:14:18) | ||
└─ at [WILDCARD]warn_on_deprecated_api/main.js:26:7 | ||
|
||
hello world | ||
Warning | ||
├ Use of deprecated "Deno.run()" API. | ||
│ | ||
├ This API will be removed in Deno 2.0. Make sure to upgrade to a stable API before then. | ||
│ | ||
├ Suggestion: Use "Deno.Command()" API instead. | ||
│ | ||
└ Stack trace: | ||
├─ at runEcho ([WILDCARD]warn_on_deprecated_api/main.js:14:18) | ||
└─ at [WILDCARD]warn_on_deprecated_api/main.js:29:9 | ||
|
||
hello world | ||
hello world | ||
hello world | ||
hello world | ||
hello world | ||
hello world | ||
hello world | ||
hello world | ||
hello world | ||
hello world | ||
Warning | ||
├ Use of deprecated "Deno.run()" API. | ||
│ | ||
├ This API will be removed in Deno 2.0. Make sure to upgrade to a stable API before then. | ||
│ | ||
├ Suggestion: Use "Deno.Command()" API instead. | ||
│ | ||
├ Suggestion: It appears this API is used by a remote dependency. | ||
│ Try upgrading to the latest version of that dependency. | ||
│ | ||
└ Stack trace: | ||
├─ at runEcho (http://localhost:4545/run/warn_on_deprecated_api/mod.ts:2:18) | ||
└─ at [WILDCARD]warn_on_deprecated_api/main.js:32:7 | ||
|
||
hello world |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export async function runEcho() { | ||
const p = Deno.run({ | ||
cmd: [ | ||
Deno.execPath(), | ||
"eval", | ||
"console.log('hello world')", | ||
], | ||
}); | ||
await p.status(); | ||
p.close(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,8 @@ const { | |
ArrayPrototypeFilter, | ||
ArrayPrototypeIncludes, | ||
ArrayPrototypeMap, | ||
ArrayPrototypePop, | ||
ArrayPrototypeShift, | ||
DateNow, | ||
Error, | ||
ErrorPrototype, | ||
|
@@ -23,6 +25,10 @@ const { | |
ObjectValues, | ||
PromisePrototypeThen, | ||
PromiseResolve, | ||
SafeSet, | ||
StringPrototypeIncludes, | ||
StringPrototypeSplit, | ||
StringPrototypeTrim, | ||
Symbol, | ||
SymbolIterator, | ||
TypeError, | ||
|
@@ -89,6 +95,88 @@ ObjectDefineProperties(Symbol, { | |
let windowIsClosing = false; | ||
let globalThis_; | ||
|
||
const ALREADY_WARNED_DEPRECATED = new SafeSet(); | ||
|
||
function warnOnDeprecatedApi(apiName, stack, suggestion) { | ||
if (ALREADY_WARNED_DEPRECATED.has(apiName + stack)) { | ||
return; | ||
} | ||
|
||
// If we haven't warned yet, let's do some processing of the stack trace | ||
// to make it more useful. | ||
const stackLines = StringPrototypeSplit(stack, "\n"); | ||
ArrayPrototypeShift(stackLines); | ||
while (true) { | ||
// Filter out internal frames at the top of the stack - they are not useful | ||
// to the user. | ||
if ( | ||
StringPrototypeIncludes(stackLines[0], "(ext:") || | ||
StringPrototypeIncludes(stackLines[0], "(node:") | ||
) { | ||
ArrayPrototypeShift(stackLines); | ||
} else { | ||
break; | ||
} | ||
} | ||
// Now remove the last frame if it's coming from "ext:core" - this is most likely | ||
// event loop tick or promise handler calling a user function - again not | ||
// useful to the user. | ||
if ( | ||
StringPrototypeIncludes(stackLines[stackLines.length - 1], "(ext:core/") | ||
) { | ||
ArrayPrototypePop(stackLines); | ||
} | ||
|
||
let isFromRemoteDependency = false; | ||
const firstStackLine = stackLines[0]; | ||
if (firstStackLine && !StringPrototypeIncludes(firstStackLine, "file:")) { | ||
isFromRemoteDependency = true; | ||
} | ||
|
||
ALREADY_WARNED_DEPRECATED.add(apiName + stack); | ||
console.log( | ||
"%cWarning", | ||
"color: yellow; font-weight: bold;", | ||
); | ||
console.log( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I want it to stand out and be sore to the eyes :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea 😆 |
||
`%c\u251c Use of deprecated "${apiName}" API.`, | ||
"color: yellow;", | ||
); | ||
console.log("%c\u2502", "color: yellow;"); | ||
console.log( | ||
"%c\u251c This API will be removed in Deno 2.0. Make sure to upgrade to a stable API before then.", | ||
"color: yellow;", | ||
); | ||
console.log("%c\u2502", "color: yellow;"); | ||
console.log( | ||
`%c\u251c Suggestion: ${suggestion}`, | ||
"color: yellow;", | ||
); | ||
if (isFromRemoteDependency) { | ||
console.log("%c\u2502", "color: yellow;"); | ||
console.log( | ||
`%c\u251c Suggestion: It appears this API is used by a remote dependency.`, | ||
"color: yellow;", | ||
); | ||
console.log( | ||
"%c\u2502 Try upgrading to the latest version of that dependency.", | ||
"color: yellow;", | ||
); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should print the ability to disable these warnings with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
console.log("%c\u2502", "color: yellow;"); | ||
console.log("%c\u2514 Stack trace:", "color: yellow;"); | ||
for (let i = 0; i < stackLines.length; i++) { | ||
console.log( | ||
`%c ${i == stackLines.length - 1 ? "\u2514" : "\u251c"}\u2500 ${ | ||
StringPrototypeTrim(stackLines[i]) | ||
}`, | ||
"color: yellow;", | ||
); | ||
} | ||
console.log(); | ||
} | ||
|
||
function windowClose() { | ||
if (!windowIsClosing) { | ||
windowIsClosing = true; | ||
|
@@ -432,7 +520,7 @@ function exposeUnstableFeaturesForWindowOrWorkerGlobalScope(options) { | |
// FIXME(bartlomieju): temporarily add whole `Deno.core` to | ||
// `Deno[Deno.internal]` namespace. It should be removed and only necessary | ||
// methods should be left there. | ||
ObjectAssign(internals, { core }); | ||
ObjectAssign(internals, { core, warnOnDeprecatedApi }); | ||
const internalSymbol = Symbol("Deno.internal"); | ||
const finalDenoNs = { | ||
internal: internalSymbol, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious why we shift one item here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to remove the
Error\n
at the start of the output fromnew Error().stack
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if that's worth the cost of
split
,shift
, andjoin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest
stack.slice(6)
with a comment.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also suggest we should do that
slice
at the line of"%cThis API was called from:\n" + stackString + "\n",
to minimize the overhead of this utilThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also filtering irrelevant stack frames. Without it the stacks are very noisy and make it hard to figure out where the API is called. It's not like this API will be called in the hot path (and if it is it's yet another reason to fix user code quickly :))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will change the logic to first check if such stack traces was produced before manipulating the stack.Done.