-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
[BUGFIX release] Refine Ember Global deprecation message #19557
Changes from all commits
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 |
---|---|---|
|
@@ -10,6 +10,7 @@ const buildStripClassCallcheckPlugin = require('./build-strip-class-callcheck-pl | |
const injectBabelHelpers = require('./transforms/inject-babel-helpers').injectBabelHelpers; | ||
const debugTree = require('broccoli-debug').buildDebugCallback('ember-source:addon'); | ||
const vmBabelPlugins = require('@glimmer/vm-babel-plugins'); | ||
const semver = require('semver'); | ||
|
||
const PRE_BUILT_TARGETS = [ | ||
'last 1 Chrome versions', | ||
|
@@ -40,6 +41,17 @@ add( | |
path.join(__dirname, '..', 'dist', 'ember-template-compiler.js') | ||
); | ||
|
||
function* walkAddonTree(project, pathToAddon = []) { | ||
for (let addon of project.addons) { | ||
yield [addon, pathToAddon]; | ||
yield* walkAddonTree(addon, [...pathToAddon, `${addon.name}@${addon.pkg.version}`]); | ||
} | ||
} | ||
|
||
function requirementFor(pkg, deps = {}) { | ||
return deps[pkg]; | ||
} | ||
|
||
module.exports = { | ||
init() { | ||
this._super.init && this._super.init.apply(this, arguments); | ||
|
@@ -64,11 +76,14 @@ module.exports = { | |
name: 'ember-source', | ||
paths, | ||
absolutePaths, | ||
_bootstrapEmber: "require('@ember/-internals/bootstrap').default();", | ||
_jqueryIntegrationEnabled: true, | ||
|
||
included() { | ||
this._super.included.apply(this, arguments); | ||
|
||
this._issueGlobalsDeprecation(); | ||
|
||
const { has } = require('@ember/edition-utils'); | ||
|
||
let optionalFeatures = this.project.addons.find((a) => a.name === '@ember/optional-features'); | ||
|
@@ -244,7 +259,7 @@ module.exports = { | |
return new MergeTrees([ | ||
concatBundle(emberFiles, { | ||
outputFile: 'ember.js', | ||
footer: "require('@ember/-internals/bootstrap');", | ||
footer: this._bootstrapEmber, | ||
}), | ||
|
||
concatBundle(emberTestingFiles, { | ||
|
@@ -309,4 +324,236 @@ module.exports = { | |
|
||
return debugTree(new MergeTrees([ember, templateCompiler, jquery]), 'vendor:final'); | ||
}, | ||
|
||
_issueGlobalsDeprecation() { | ||
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. This function looks quite complex, but I don't see any tests ensuring it is both accurate and remains accurate overtime. Are these present elsewhere or is this an oversight? |
||
if (process.env.EMBER_ENV === 'production') { | ||
return; | ||
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. Don't we still need to set the bootstrap string? 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 is a default (same as the current one) on the prototype (?) |
||
} | ||
|
||
let isYarnProject = ((root) => { | ||
try { | ||
// eslint-disable-next-line node/no-unpublished-require | ||
return require('ember-cli/lib/utilities/is-yarn-project')(root); | ||
} catch { | ||
return undefined; | ||
} | ||
})(this.project.root); | ||
|
||
let groupedByTopLevelAddon = Object.create(null); | ||
let groupedByVersion = Object.create(null); | ||
let projectInfo; | ||
|
||
for (let [addon, pathToAddon] of walkAddonTree(this.project)) { | ||
let version = addon.pkg.version; | ||
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 may (speculation, and untested) want to skip the current iteration if the But if the |
||
|
||
if (addon.name === 'ember-cli-babel' && semver.lt(version, '7.26.6')) { | ||
let info; | ||
|
||
if (addon.parent === this.project) { | ||
let requirement = requirementFor('ember-cli-babel', this.project.pkg.devDependencies); | ||
let compatible = semver.satisfies('7.26.6', requirement); | ||
|
||
info = projectInfo = { | ||
parent: `${this.project.name()} (your app)`, | ||
version, | ||
requirement, | ||
compatible, | ||
dormant: false, | ||
path: pathToAddon, | ||
}; | ||
} else { | ||
let requirement = requirementFor('ember-cli-babel', addon.parent.pkg.dependencies); | ||
let compatible = semver.satisfies('7.26.6', requirement); | ||
let dormant = addon.parent._fileSystemInfo | ||
? addon.parent._fileSystemInfo().hasJSFiles === false | ||
: false; | ||
|
||
let topLevelAddon = addon.parent; | ||
|
||
while (topLevelAddon.parent !== this.project) { | ||
topLevelAddon = topLevelAddon.parent; | ||
} | ||
|
||
info = { | ||
parent: `${addon.parent.name}@${addon.pkg.version}`, | ||
version, | ||
requirement, | ||
compatible, | ||
dormant, | ||
path: pathToAddon, | ||
}; | ||
|
||
let addons = groupedByTopLevelAddon[topLevelAddon.name] || []; | ||
groupedByTopLevelAddon[topLevelAddon.name] = [...addons, info]; | ||
} | ||
|
||
let group = groupedByVersion[version] || Object.create(null); | ||
groupedByVersion[version] = group; | ||
|
||
let addons = group[info.parent] || []; | ||
group[info.parent] = [...addons, info]; | ||
} | ||
} | ||
|
||
if (Object.keys(groupedByVersion).length === 0) { | ||
return; | ||
} | ||
|
||
let dormantTopLevelAddons = []; | ||
let compatibleTopLevelAddons = []; | ||
let incompatibleTopLevelAddons = []; | ||
|
||
for (let addon of Object.keys(groupedByTopLevelAddon)) { | ||
let group = groupedByTopLevelAddon[addon]; | ||
|
||
if (group.every((info) => info.dormant)) { | ||
dormantTopLevelAddons.push(addon); | ||
} else if (group.every((info) => info.compatible)) { | ||
compatibleTopLevelAddons.push(addon); | ||
} else { | ||
incompatibleTopLevelAddons.push(addon); | ||
} | ||
} | ||
|
||
let message = | ||
'Usage of the Ember Global is deprecated. ' + | ||
'You should import the Ember module or the specific API instead.\n\n' + | ||
'See https://deprecations.emberjs.com/v3.x/#toc_ember-global for details.\n\n' + | ||
'Usages of the Ember Global may be caused by an outdated ember-cli-babel dependency. ' + | ||
'The following steps may help:\n\n'; | ||
|
||
let hasActionableSteps = false; | ||
|
||
if (projectInfo) { | ||
message += '* Upgrade your `devDependencies` on `ember-cli-babel` to `^7.26.6`.\n'; | ||
hasActionableSteps = true; | ||
} else if (compatibleTopLevelAddons.length > 0) { | ||
// Only show the compatible addons if the project itself is up-to-date, because updating the | ||
// project's own dependency on ember-cli-babel to latest may also get these addons to use it | ||
// as well. Otherwise, there is an unnecessary copy in the tree and it needs to be deduped. | ||
if (isYarnProject === true) { | ||
message += | ||
'* Run `npx yarn-deduplicate --packages ember-cli-babel` followed by `yarn install`.\n'; | ||
} else if (isYarnProject === false) { | ||
message += '* Run `npm dedupe`.\n'; | ||
} else { | ||
message += | ||
'* If using yarn, run `npx yarn-deduplicate --packages ember-cli-babel` followed by `yarn install`.\n' + | ||
'* If using npm, run `npm dedupe`.\n'; | ||
} | ||
|
||
hasActionableSteps = true; | ||
} | ||
|
||
if (incompatibleTopLevelAddons.length > 0) { | ||
message += '* Upgrade the following addons to the latest version:\n'; | ||
|
||
for (let addon of incompatibleTopLevelAddons) { | ||
message += ` * ${addon}\n`; | ||
} | ||
|
||
hasActionableSteps = true; | ||
} | ||
|
||
if (!hasActionableSteps) { | ||
// Only show the dormant addons if there are nothing else to do because they are unlikely to | ||
// be the problem. | ||
message += '* Upgrade the following addons to the latest version, if available:\n'; | ||
|
||
for (let addon of dormantTopLevelAddons) { | ||
message += ` * ${addon}\n`; | ||
} | ||
} | ||
|
||
if (hasActionableSteps && process.env.EMBER_GLOBAL_DEPRECATIONS !== 'all') { | ||
message += | ||
'\n### Important ###\n\n' + | ||
'In order to avoid repeatedly showing the same deprecation messages, ' + | ||
'no further deprecation messages will be shown for usages of the Ember Global ' + | ||
'until ember-cli-babel is upgraded to v7.26.6 or above.\n\n' + | ||
'To see all instances of this deprecation message at runtime, ' + | ||
'set the `EMBER_GLOBAL_DEPRECATIONS` environment variable to "all", ' + | ||
'e.g. `EMBER_GLOBAL_DEPRECATIONS=all ember test`.\n'; | ||
} | ||
|
||
message += | ||
'\n### Details ###\n\n' + | ||
'Prior to v7.26.6, ember-cli-babel sometimes transpiled imports into the equivalent Ember Global API, ' + | ||
'potentially triggering this deprecation message even when you did not directly reference the Ember Global.\n\n' + | ||
'The following outdated versions are found in your project:\n'; | ||
|
||
let hasDormantAddons = false; | ||
let hasCompatibleAddons = false; | ||
|
||
for (let version of Object.keys(groupedByVersion).sort(semver.compare)) { | ||
message += `\n* ember-cli-babel@${version}, currently used by:\n`; | ||
|
||
for (let parent of Object.keys(groupedByVersion[version]).sort()) { | ||
let info = groupedByVersion[version][parent][0]; | ||
|
||
message += ` * ${parent}`; | ||
|
||
if (info.dormant) { | ||
message += ' (Dormant)\n'; | ||
hasDormantAddons = true; | ||
} else if (info.compatible) { | ||
message += ' (Compatible)\n'; | ||
hasCompatibleAddons = true; | ||
} else { | ||
message += '\n'; | ||
} | ||
|
||
message += ` * Depends on ember-cli-babel@${groupedByVersion[version][parent][0].requirement}\n`; | ||
|
||
for (let info of groupedByVersion[version][parent]) { | ||
let adddedBy = info.path.slice(0, -1); | ||
|
||
if (adddedBy.length) { | ||
message += ` * Added by ${adddedBy.join(' > ')}\n`; | ||
} | ||
|
||
if (info.compatible) { | ||
hasCompatibleAddons = true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (hasDormantAddons) { | ||
message += | ||
'\nNote: Addons marked as "Dormant" does not appear to have any JavaScript files. ' + | ||
'Therefore, even if they are using an old version ember-cli-babel, they are ' + | ||
'unlikely to be the cuplrit of this deprecation and can likely be ignored.\n'; | ||
} | ||
|
||
if (hasCompatibleAddons) { | ||
message += `\nNote: Addons marked as "Compatible" are already compatible with ember-cli-babel@7.26.6. `; | ||
|
||
if (projectInfo) { | ||
message += 'Try upgrading your `devDependencies` on `ember-cli-babel` to `^7.26.6`.\n'; | ||
} else { | ||
if (isYarnProject === true) { | ||
message += | ||
'Try running `npx yarn-deduplicate --packages ember-cli-babel` followed by `yarn install`.\n'; | ||
} else if (isYarnProject === false) { | ||
message += 'Try running `npm dedupe`.\n'; | ||
} else { | ||
message += | ||
'If using yarn, try running `npx yarn-deduplicate --packages ember-cli-babel` followed by `yarn install`.' + | ||
'If using npm, try running `npm dedupe`.\n'; | ||
} | ||
} | ||
} | ||
|
||
if (hasActionableSteps) { | ||
this.ui.writeWarnLine('[DEPRECATION] ' + message); | ||
} | ||
|
||
this._bootstrapEmber = ` | ||
require('@ember/-internals/bootstrap').default( | ||
${JSON.stringify(message)}, | ||
${hasActionableSteps && process.env.EMBER_GLOBAL_DEPRECATIONS !== 'all'} | ||
); | ||
`; | ||
}, | ||
}; |
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.
@rwjblue
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.
Ah, gotcha sorry