➡️ Migrate core package 'deprecation-cop' into ./packages

This commit is contained in:
David Wilson
2018-10-01 16:12:43 -07:00
parent 2e9e57fa13
commit 692f6c344e
16 changed files with 951 additions and 10 deletions

9
package-lock.json generated
View File

@@ -1712,8 +1712,7 @@
}
},
"deprecation-cop": {
"version": "https://www.atom.io/api/packages/deprecation-cop/versions/0.56.9/tarball",
"integrity": "sha512-dTKNhWcDgK6Y5cR8dwZ507QW15lob+Lp//P71wXoTVidXboDqH13Y1yQ7Av5qASscv7fqp5GcvLhQWF55W5yng==",
"version": "file:packages/deprecation-cop",
"requires": {
"etch": "0.9.0",
"fs-plus": "^3.0.0",
@@ -1724,13 +1723,11 @@
"dependencies": {
"etch": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz",
"integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8="
"bundled": true
},
"grim": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz",
"integrity": "sha1-52CinKe4NDsMH/r2ziDyGkbuiu0=",
"bundled": true,
"requires": {
"event-kit": "^2.0.0"
}

View File

@@ -49,7 +49,7 @@
"command-palette": "https://www.atom.io/api/packages/command-palette/versions/0.43.5/tarball",
"dalek": "https://www.atom.io/api/packages/dalek/versions/0.2.2/tarball",
"dedent": "^0.7.0",
"deprecation-cop": "https://www.atom.io/api/packages/deprecation-cop/versions/0.56.9/tarball",
"deprecation-cop": "file:packages/deprecation-cop",
"dev-live-reload": "file:packages/dev-live-reload",
"devtron": "1.3.0",
"encoding-selector": "https://www.atom.io/api/packages/encoding-selector/versions/0.23.9/tarball",
@@ -196,7 +196,7 @@
"bracket-matcher": "0.89.3",
"command-palette": "0.43.5",
"dalek": "0.2.2",
"deprecation-cop": "0.56.9",
"deprecation-cop": "file:./packages/deprecation-cop",
"dev-live-reload": "file:./packages/dev-live-reload",
"encoding-selector": "0.23.9",
"exception-reporting": "file:./packages/exception-reporting",

View File

@@ -27,7 +27,7 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
| **bracket-matcher** | [`atom/bracket-matcher`][bracket-matcher] | |
| **command-palette** | [`atom/command-palette`][command-palette] | |
| **dalek** | [`atom/dalek`][dalek] | [#17838](https://github.com/atom/atom/issues/17838) |
| **deprecation-cop** | [`atom/deprecation-cop`][deprecation-cop] | [#17839](https://github.com/atom/atom/issues/17839) |
| **deprecation-cop** | [`./deprecation-cop`](./deprecation-cop) | [#17839](https://github.com/atom/atom/issues/17839) |
| **dev-live-reload** | [`./dev-live-reload`](dev-live-reload) | [#17840](https://github.com/atom/atom/issues/17840) |
| **encoding-selector** | [`atom/encoding-selector`][encoding-selector] | [#17841](https://github.com/atom/atom/issues/17841) |
| **exception-reporting** | [`./exception-reporting`](./exception-reporting) | [#17842](https://github.com/atom/atom/issues/17842) |
@@ -114,7 +114,6 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
[bracket-matcher]: https://github.com/atom/bracket-matcher
[command-palette]: https://github.com/atom/command-palette
[dalek]: https://github.com/atom/dalek
[deprecation-cop]: https://github.com/atom/deprecation-cop
[encoding-selector]: https://github.com/atom/encoding-selector
[find-and-replace]: https://github.com/atom/find-and-replace
[fuzzy-finder]: https://github.com/atom/fuzzy-finder

View File

@@ -0,0 +1 @@
spec/fixtures

3
packages/deprecation-cop/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.DS_Store
npm-debug.log
node_modules

View File

@@ -0,0 +1,20 @@
Copyright (c) 2014 <Your name here>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,6 @@
# Deprecation Cop package
[![OS X Build Status](https://travis-ci.org/atom/deprecation-cop.svg?branch=master)](https://travis-ci.org/atom/deprecation-cop) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/0s870q5fj3vwihjx/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/deprecation-cop/branch/master) [![Dependency Status](https://david-dm.org/atom/deprecation-cop.svg)](https://david-dm.org/atom/deprecation-cop)
Shows a list of deprecated methods calls. Ideally it should show nothing!
![https://github-images.s3.amazonaws.com/skitch/Deprecation_Cop_-__Users_corey_github_deprecation-cop-20140414-144618.jpg](https://github-images.s3.amazonaws.com/skitch/Deprecation_Cop_-__Users_corey_github_deprecation-cop-20140414-144618.jpg)

View File

@@ -0,0 +1,37 @@
{
"max_line_length": {
"level": "ignore"
},
"no_empty_param_list": {
"level": "error"
},
"arrow_spacing": {
"level": "error"
},
"no_interpolation_in_single_quotes": {
"level": "error"
},
"no_debugger": {
"level": "error"
},
"prefer_english_operator": {
"level": "error"
},
"colon_assignment_spacing": {
"spacing": {
"left": 0,
"right": 1
},
"level": "error"
},
"braces_spacing": {
"spaces": 0,
"level": "error"
},
"spacing_after_comma": {
"level": "error"
},
"no_stand_alone_at": {
"level": "error"
}
}

View File

@@ -0,0 +1,68 @@
{CompositeDisposable, Disposable} = require 'atom'
_ = require 'underscore-plus'
Grim = require 'grim'
module.exports =
class DeprecationCopStatusBarView
lastLength: null
toolTipDisposable: null
constructor: ->
@subscriptions = new CompositeDisposable
@element = document.createElement('div')
@element.classList.add('deprecation-cop-status', 'inline-block', 'text-warning')
@element.setAttribute('tabindex', -1)
@icon = document.createElement('span')
@icon.classList.add('icon', 'icon-alert')
@element.appendChild(@icon)
@deprecationNumber = document.createElement('span')
@deprecationNumber.classList.add('deprecation-number')
@deprecationNumber.textContent = '0'
@element.appendChild(@deprecationNumber)
clickHandler = ->
workspaceElement = atom.views.getView(atom.workspace)
atom.commands.dispatch workspaceElement, 'deprecation-cop:view'
@element.addEventListener('click', clickHandler)
@subscriptions.add(new Disposable(=> @element.removeEventListener('click', clickHandler)))
@update()
debouncedUpdateDeprecatedSelectorCount = _.debounce(@update, 1000)
@subscriptions.add Grim.on 'updated', @update
# TODO: Remove conditional when the new StyleManager deprecation APIs reach stable.
if atom.styles.onDidUpdateDeprecations?
@subscriptions.add(atom.styles.onDidUpdateDeprecations(debouncedUpdateDeprecatedSelectorCount))
destroy: ->
@subscriptions.dispose()
@element.remove()
getDeprecatedCallCount: ->
Grim.getDeprecations().map((d) -> d.getStackCount()).reduce(((a, b) -> a + b), 0)
getDeprecatedStyleSheetsCount: ->
# TODO: Remove conditional when the new StyleManager deprecation APIs reach stable.
if atom.styles.getDeprecations?
Object.keys(atom.styles.getDeprecations()).length
else
0
update: =>
length = @getDeprecatedCallCount() + @getDeprecatedStyleSheetsCount()
return if @lastLength is length
@lastLength = length
@deprecationNumber.textContent = "#{_.pluralize(length, 'deprecation')}"
@toolTipDisposable?.dispose()
@toolTipDisposable = atom.tooltips.add @element, title: "#{_.pluralize(length, 'call')} to deprecated methods"
if length is 0
@element.style.display = 'none'
else
@element.style.display = ''

View File

@@ -0,0 +1,467 @@
/** @babel */
/** @jsx etch.dom */
import _ from 'underscore-plus'
import {CompositeDisposable} from 'atom'
import etch from 'etch'
import fs from 'fs-plus'
import Grim from 'grim'
import marked from 'marked'
import path from 'path'
import shell from 'shell'
export default class DeprecationCopView {
constructor ({uri}) {
this.uri = uri
this.subscriptions = new CompositeDisposable
this.subscriptions.add(Grim.on('updated', () => { etch.update(this) }))
// TODO: Remove conditional when the new StyleManager deprecation APIs reach stable.
if (atom.styles.onDidUpdateDeprecations) {
this.subscriptions.add(atom.styles.onDidUpdateDeprecations(() => { etch.update(this) }))
}
etch.initialize(this)
this.subscriptions.add(atom.commands.add(this.element, {
'core:move-up': () => { this.scrollUp() },
'core:move-down': () => { this.scrollDown() },
'core:page-up': () => { this.pageUp() },
'core:page-down': () => { this.pageDown() },
'core:move-to-top': () => { this.scrollToTop() },
'core:move-to-bottom': () => { this.scrollToBottom() }
}))
}
serialize () {
return {
deserializer: this.constructor.name,
uri: this.getURI(),
version: 1
}
}
destroy () {
this.subscriptions.dispose()
return etch.destroy(this)
}
update () {
return etch.update(this)
}
render () {
return (
<div className='deprecation-cop pane-item native-key-bindings' tabIndex='-1'>
<div className='panel'>
<div className='padded deprecation-overview'>
<div className='pull-right btn-group'>
<button
className='btn btn-primary check-for-update'
onclick={(event) => {
event.preventDefault()
this.checkForUpdates()
}}>Check for Updates</button>
</div>
</div>
<div className='panel-heading'><span>Deprecated calls</span></div>
<ul className='list-tree has-collapsable-children'>
{this.renderDeprecatedCalls()}
</ul>
<div className='panel-heading'><span>Deprecated selectors</span></div>
<ul className='selectors list-tree has-collapsable-children'>
{this.renderDeprecatedSelectors()}
</ul>
</div>
</div>
)
}
renderDeprecatedCalls () {
const deprecationsByPackageName = this.getDeprecatedCallsByPackageName()
const packageNames = Object.keys(deprecationsByPackageName)
if (packageNames.length === 0) {
return <li className='list-item'>No deprecated calls</li>
} else {
return packageNames.sort().map((packageName) => (
<li className='deprecation list-nested-item collapsed'>
<div className='deprecation-info list-item' onclick={(event) => event.target.parentElement.classList.toggle('collapsed')}>
<span className='text-highlight'>{packageName || 'atom core'}</span>
<span>{` (${_.pluralize(deprecationsByPackageName[packageName].length, 'deprecation')})`}</span>
</div>
<ul className='list'>
{this.renderPackageActionsIfNeeded(packageName)}
{deprecationsByPackageName[packageName].map(({deprecation, stack}) => (
<li className='list-item deprecation-detail'>
<span className='text-warning icon icon-alert' />
<div className='list-item deprecation-message' innerHTML={marked(deprecation.getMessage())} />
{this.renderIssueURLIfNeeded(packageName, deprecation, this.buildIssueURL(packageName, deprecation, stack))}
<div className='stack-trace'>
{stack.map(({functionName, location}) => (
<div className='stack-line'>
<span>{functionName}</span>
<span> - </span>
<a
className='stack-line-location'
href={location}
onclick={(event) => {
event.preventDefault()
this.openLocation(location)
}}>{location}</a>
</div>
))}
</div>
</li>
))}
</ul>
</li>
))
}
}
renderDeprecatedSelectors () {
const deprecationsByPackageName = this.getDeprecatedSelectorsByPackageName()
const packageNames = Object.keys(deprecationsByPackageName)
if (packageNames.length === 0) {
return (
<li className='list-item'>No deprecated selectors</li>
)
} else {
return packageNames.map((packageName) => (
<li className='deprecation list-nested-item collapsed'>
<div className='deprecation-info list-item' onclick={(event) => event.target.parentElement.classList.toggle('collapsed')}>
<span className='text-highlight'>{packageName}</span>
</div>
<ul className='list'>
{this.renderPackageActionsIfNeeded(packageName)}
{deprecationsByPackageName[packageName].map(({packagePath, sourcePath, deprecation}) => {
const relativeSourcePath = path.relative(packagePath, sourcePath)
const issueTitle = `Deprecated selector in \`${relativeSourcePath}\``
const issueBody = `In \`${relativeSourcePath}\`: \n\n${deprecation.message}`
return (
<li className='list-item source-file'>
<a
className='source-url'
href={sourcePath}
onclick={(event) => {
event.preventDefault()
this.openLocation(sourcePath)
}}>{relativeSourcePath}</a>
<ul className='list'>
<li className='list-item deprecation-detail'>
<span className='text-warning icon icon-alert' />
<div className='list-item deprecation-message' innerHTML={marked(deprecation.message)} />
{this.renderSelectorIssueURLIfNeeded(packageName, issueTitle, issueBody)}
</li>
</ul>
</li>
)
})}
</ul>
</li>
))
}
}
renderPackageActionsIfNeeded (packageName) {
if (packageName && atom.packages.getLoadedPackage(packageName)) {
return (
<div className='padded'>
<div className='btn-group'>
<button
className='btn check-for-update'
onclick={(event) => {
event.preventDefault()
this.checkForUpdates()
}}>Check for Update</button>
<button
className='btn disable-package'
data-package-name={packageName}
onclick={(event) => {
event.preventDefault()
this.disablePackage(packageName)
}}>Disable Package</button>
</div>
</div>
)
} else {
return ''
}
}
encodeURI (str) {
return encodeURI(str).replace(/#/g, '%23').replace(/;/g, '%3B').replace(/%20/g, '+')
}
renderSelectorIssueURLIfNeeded (packageName, issueTitle, issueBody) {
const repoURL = this.getRepoURL(packageName)
if (repoURL) {
const issueURL = `${repoURL}/issues/new?title=${this.encodeURI(issueTitle)}&body=${this.encodeURI(issueBody)}`
return (
<div className='btn-toolbar'>
<button
className='btn issue-url'
data-issue-title={issueTitle}
data-repo-url={repoURL}
data-issue-url={issueURL}
onclick={(event) => {
event.preventDefault()
this.openIssueURL(repoURL, issueURL, issueTitle)
}}>Report Issue</button>
</div>
)
} else {
return ''
}
}
renderIssueURLIfNeeded (packageName, deprecation, issueURL) {
if (packageName && issueURL) {
const repoURL = this.getRepoURL(packageName)
const issueTitle = `${deprecation.getOriginName()} is deprecated.`
return (
<div className='btn-toolbar'>
<button
className='btn issue-url'
data-issue-title={issueTitle}
data-repo-url={repoURL}
data-issue-url={issueURL}
onclick={(event) => {
event.preventDefault()
this.openIssueURL(repoURL, issueURL, issueTitle)
}}>Report Issue</button>
</div>
)
} else {
return ''
}
}
buildIssueURL (packageName, deprecation, stack) {
const repoURL = this.getRepoURL(packageName)
if (repoURL) {
const title = `${deprecation.getOriginName()} is deprecated.`
const stacktrace = stack.map(({functionName, location}) => `${functionName} (${location})`).join("\n")
const body = `${deprecation.getMessage()}\n\`\`\`\n${stacktrace}\n\`\`\``
return `${repoURL}/issues/new?title=${encodeURI(title)}&body=${encodeURI(body)}`
} else {
return null
}
}
async openIssueURL (repoURL, issueURL, issueTitle) {
const issue = await this.findSimilarIssue(repoURL, issueTitle)
if (issue) {
shell.openExternal(issue.html_url)
} else if (process.platform === 'win32') {
// Windows will not launch URLs greater than ~2000 bytes so we need to shrink it
shell.openExternal((await this.shortenURL(issueURL)) || issueURL)
} else {
shell.openExternal(issueURL)
}
}
async findSimilarIssue (repoURL, issueTitle) {
const url = 'https://api.github.com/search/issues'
const repo = repoURL.replace(/http(s)?:\/\/(\d+\.)?github.com\//gi, '')
const query = `${issueTitle} repo:${repo}`
const response = await window.fetch(`${url}?q=${encodeURI(query)}&sort=created`, {
method: 'GET',
headers: {
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json'
}
})
if (response.ok) {
const data = await response.json()
if (data.items) {
const issues = {}
for (const issue of data.items) {
if (issue.title.includes(issueTitle) && !issues[issue.state]) {
issues[issue.state] = issue
}
}
return (issues.open || issues.closed)
}
}
}
async shortenURL (url) {
let encodedUrl = encodeURIComponent(url).substr(0, 5000) // is.gd has 5000 char limit
let incompletePercentEncoding = encodedUrl.indexOf('%', encodedUrl.length - 2)
if (incompletePercentEncoding >= 0) { // Handle an incomplete % encoding cut-off
encodedUrl = encodedUrl.substr(0, incompletePercentEncoding)
}
let result = await fetch('https://is.gd/create.php?format=simple', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: `url=${encodedUrl}`
})
return result.text()
}
getRepoURL (packageName) {
const loadedPackage = atom.packages.getLoadedPackage(packageName)
if (loadedPackage && loadedPackage.metadata && loadedPackage.metadata.repository) {
const url = loadedPackage.metadata.repository.url || loadedPackage.metadata.repository
return url.replace(/\.git$/, '')
} else {
return null
}
}
getDeprecatedCallsByPackageName () {
const deprecatedCalls = Grim.getDeprecations()
deprecatedCalls.sort((a, b) => b.getCallCount() - a.getCallCount())
const deprecatedCallsByPackageName = {}
for (const deprecation of deprecatedCalls) {
const stacks = deprecation.getStacks()
stacks.sort((a, b) => b.callCount - a.callCount)
for (const stack of stacks) {
let packageName = null
if (stack.metadata && stack.metadata.packageName) {
packageName = stack.metadata.packageName
} else {
packageName = (this.getPackageName(stack) || '').toLowerCase()
}
deprecatedCallsByPackageName[packageName] = deprecatedCallsByPackageName[packageName] || []
deprecatedCallsByPackageName[packageName].push({deprecation, stack})
}
}
return deprecatedCallsByPackageName
}
getDeprecatedSelectorsByPackageName () {
const deprecatedSelectorsByPackageName = {}
if (atom.styles.getDeprecations) {
const deprecatedSelectorsBySourcePath = atom.styles.getDeprecations()
for (const sourcePath of Object.keys(deprecatedSelectorsBySourcePath)) {
const deprecation = deprecatedSelectorsBySourcePath[sourcePath]
const components = sourcePath.split(path.sep)
const packagesComponentIndex = components.indexOf('packages')
let packageName = null
let packagePath = null
if (packagesComponentIndex === -1) {
packageName = 'Other' // could be Atom Core or the personal style sheet
packagePath = ''
} else {
packageName = components[packagesComponentIndex + 1]
packagePath = components.slice(0, packagesComponentIndex + 1).join(path.sep)
}
deprecatedSelectorsByPackageName[packageName] = deprecatedSelectorsByPackageName[packageName] || []
deprecatedSelectorsByPackageName[packageName].push({packagePath, sourcePath, deprecation})
}
}
return deprecatedSelectorsByPackageName
}
getPackageName (stack) {
const packagePaths = this.getPackagePathsByPackageName()
for (const [packageName, packagePath] of packagePaths) {
if (packagePath.includes('.atom/dev/packages') || packagePath.includes('.atom/packages')) {
packagePaths.set(packageName, fs.absolute(packagePath))
}
}
for (let i = 1; i < stack.length; i++) {
const {fileName} = stack[i]
// Empty when it was run from the dev console
if (!fileName) {
return null
}
// Continue to next stack entry if call is in node_modules
if (fileName.includes(`${path.sep}node_modules${path.sep}`)) {
continue
}
for (const [packageName, packagePath] of packagePaths) {
const relativePath = path.relative(packagePath, fileName)
if (!/^\.\./.test(relativePath)) {
return packageName
}
}
if (atom.getUserInitScriptPath() === fileName) {
return `Your local ${path.basename(fileName)} file`
}
}
return null
}
getPackagePathsByPackageName () {
if (this.packagePathsByPackageName) {
return this.packagePathsByPackageName
} else {
this.packagePathsByPackageName = new Map()
for (const pack of atom.packages.getLoadedPackages()) {
this.packagePathsByPackageName.set(pack.name, pack.path)
}
return this.packagePathsByPackageName
}
}
checkForUpdates () {
atom.workspace.open('atom://config/updates')
}
disablePackage (packageName) {
if (packageName) {
atom.packages.disablePackage(packageName)
}
}
openLocation (location) {
let pathToOpen = location.replace('file://', '')
if (process.platform === 'win32') {
pathToOpen = pathToOpen.replace(/^\//, '')
}
atom.open({pathsToOpen: [pathToOpen]})
}
getURI () {
return this.uri
}
getTitle () {
return 'Deprecation Cop'
}
getIconName () {
return 'alert'
}
scrollUp () {
this.element.scrollTop -= document.body.offsetHeight / 20
}
scrollDown () {
this.element.scrollTop += document.body.offsetHeight / 20
}
pageUp () {
this.element.scrollTop -= this.element.offsetHeight
}
pageDown () {
this.element.scrollTop += this.element.offsetHeight
}
scrollToTop () {
this.element.scrollTop = 0
}
scrollToBottom () {
this.element.scrollTop = this.element.scrollHeight
}
}

View File

@@ -0,0 +1,39 @@
const {Disposable, CompositeDisposable} = require('atom')
const DeprecationCopView = require('./deprecation-cop-view')
const DeprecationCopStatusBarView = require('./deprecation-cop-status-bar-view')
const ViewURI = 'atom://deprecation-cop'
class DeprecationCopPackage {
activate () {
this.disposables = new CompositeDisposable()
this.disposables.add(atom.workspace.addOpener((uri) => {
if (uri === ViewURI) {
return this.deserializeDeprecationCopView({uri})
}
}))
this.disposables.add(atom.commands.add('atom-workspace', 'deprecation-cop:view', () => {
atom.workspace.open(ViewURI)
}))
}
deactivate () {
this.disposables.dispose()
const pane = atom.workspace.paneForURI(ViewURI)
if (pane) {
pane.destroyItem(pane.itemForURI(ViewURI))
}
}
deserializeDeprecationCopView (state) {
return new DeprecationCopView(state)
}
consumeStatusBar (statusBar) {
const statusBarView = new DeprecationCopStatusBarView()
const statusBarTile = statusBar.addRightTile({item: statusBarView, priority: 150})
this.disposables.add(new Disposable(() => { statusBarView.destroy() }))
this.disposables.add(new Disposable(() => { statusBarTile.destroy() }))
}
}
module.exports = new DeprecationCopPackage()

View File

@@ -0,0 +1,31 @@
{
"name": "deprecation-cop",
"main": "./lib/main",
"version": "0.56.9",
"description": "Shows a list of deprecated calls",
"repository": "https://github.com/atom/atom",
"license": "MIT",
"engines": {
"atom": ">0.50.0"
},
"dependencies": {
"etch": "0.9.0",
"fs-plus": "^3.0.0",
"grim": "^2.0.1",
"marked": "^0.3.6",
"underscore-plus": "^1.0.0"
},
"consumedServices": {
"status-bar": {
"versions": {
"^1.0.0": "consumeStatusBar"
}
}
},
"deserializers": {
"DeprecationCopView": "deserializeDeprecationCopView"
},
"devDependencies": {
"coffeelint": "^1.9.7"
}
}

View File

@@ -0,0 +1,36 @@
DeprecationCopView = require '../lib/deprecation-cop-view'
describe "DeprecationCop", ->
[activationPromise, workspaceElement] = []
beforeEach ->
workspaceElement = atom.views.getView(atom.workspace)
activationPromise = atom.packages.activatePackage('deprecation-cop')
expect(atom.workspace.getActivePane().getActiveItem()).not.toExist()
describe "when the deprecation-cop:view event is triggered", ->
it "displays the deprecation cop pane", ->
atom.commands.dispatch workspaceElement, 'deprecation-cop:view'
waitsForPromise ->
activationPromise
deprecationCopView = null
waitsFor ->
deprecationCopView = atom.workspace.getActivePane().getActiveItem()
runs ->
expect(deprecationCopView instanceof DeprecationCopView).toBeTruthy()
describe "deactivating the package", ->
it "removes the deprecation cop pane item", ->
atom.commands.dispatch workspaceElement, 'deprecation-cop:view'
waitsForPromise ->
activationPromise
waitsForPromise ->
Promise.resolve(atom.packages.deactivatePackage('deprecation-cop')) # Wrapped for Promise & non-Promise deactivate
runs ->
expect(atom.workspace.getActivePane().getActiveItem()).not.toExist()

View File

@@ -0,0 +1,72 @@
path = require 'path'
Grim = require 'grim'
DeprecationCopView = require '../lib/deprecation-cop-view'
_ = require 'underscore-plus'
describe "DeprecationCopStatusBarView", ->
[deprecatedMethod, statusBarView, workspaceElement] = []
beforeEach ->
# jasmine.Clock.useMock() cannot mock _.debounce
# http://stackoverflow.com/questions/13707047/spec-for-async-functions-using-jasmine
spyOn(_, 'debounce').andCallFake (func) ->
-> func.apply(this, arguments)
jasmine.snapshotDeprecations()
workspaceElement = atom.views.getView(atom.workspace)
jasmine.attachToDOM(workspaceElement)
waitsForPromise -> atom.packages.activatePackage('status-bar')
waitsForPromise -> atom.packages.activatePackage('deprecation-cop')
waitsFor ->
statusBarView = workspaceElement.querySelector('.deprecation-cop-status')
afterEach ->
jasmine.restoreDeprecationsSnapshot()
it "adds the status bar view when activated", ->
expect(statusBarView).toExist()
expect(statusBarView.textContent).toBe '0 deprecations'
expect(statusBarView).not.toShow()
it "increments when there are deprecated methods", ->
deprecatedMethod = -> Grim.deprecate("This isn't used")
anotherDeprecatedMethod = -> Grim.deprecate("This either")
expect(statusBarView.style.display).toBe 'none'
expect(statusBarView.offsetHeight).toBe(0)
deprecatedMethod()
expect(statusBarView.textContent).toBe '1 deprecation'
expect(statusBarView.offsetHeight).toBeGreaterThan(0)
deprecatedMethod()
expect(statusBarView.textContent).toBe '2 deprecations'
expect(statusBarView.offsetHeight).toBeGreaterThan(0)
anotherDeprecatedMethod()
expect(statusBarView.textContent).toBe '3 deprecations'
expect(statusBarView.offsetHeight).toBeGreaterThan(0)
# TODO: Remove conditional when the new StyleManager deprecation APIs reach stable.
if atom.styles.getDeprecations?
it "increments when there are deprecated selectors", ->
atom.styles.addStyleSheet("""
atom-text-editor::shadow { color: red; }
""", sourcePath: 'file-1')
expect(statusBarView.textContent).toBe '1 deprecation'
expect(statusBarView).toBeVisible()
atom.styles.addStyleSheet("""
atom-text-editor::shadow { color: blue; }
""", sourcePath: 'file-2')
expect(statusBarView.textContent).toBe '2 deprecations'
expect(statusBarView).toBeVisible()
it 'opens deprecation cop tab when clicked', ->
expect(atom.workspace.getActivePane().getActiveItem()).not.toExist()
waitsFor (done) ->
atom.workspace.onDidOpen ({item}) ->
expect(item instanceof DeprecationCopView).toBe true
done()
statusBarView.click()

View File

@@ -0,0 +1,93 @@
Grim = require 'grim'
path = require 'path'
_ = require 'underscore-plus'
etch = require 'etch'
describe "DeprecationCopView", ->
[deprecationCopView, workspaceElement] = []
beforeEach ->
spyOn(_, 'debounce').andCallFake (func) ->
-> func.apply(this, arguments)
workspaceElement = atom.views.getView(atom.workspace)
jasmine.attachToDOM(workspaceElement)
jasmine.snapshotDeprecations()
Grim.clearDeprecations()
deprecatedMethod = -> Grim.deprecate("A test deprecation. This isn't used")
deprecatedMethod()
spyOn(Grim, 'deprecate') # Don't fail tests if when using deprecated APIs in deprecation cop's activation
activationPromise = atom.packages.activatePackage('deprecation-cop')
atom.commands.dispatch workspaceElement, 'deprecation-cop:view'
waitsForPromise ->
activationPromise
waitsFor -> deprecationCopView = atom.workspace.getActivePane().getActiveItem()
runs ->
jasmine.unspy(Grim, 'deprecate')
afterEach ->
jasmine.restoreDeprecationsSnapshot()
it "displays deprecated methods", ->
expect(deprecationCopView.element.textContent).toMatch /Deprecated calls/
expect(deprecationCopView.element.textContent).toMatch /This isn't used/
# TODO: Remove conditional when the new StyleManager deprecation APIs reach stable.
if atom.styles.getDeprecations?
it "displays deprecated selectors", ->
atom.styles.addStyleSheet("atom-text-editor::shadow { color: red }", sourcePath: path.join('some-dir', 'packages', 'package-1', 'file-1.css'))
atom.styles.addStyleSheet("atom-text-editor::shadow { color: yellow }", context: 'atom-text-editor', sourcePath: path.join('some-dir', 'packages', 'package-1', 'file-2.css'))
atom.styles.addStyleSheet('atom-text-editor::shadow { color: blue }', sourcePath: path.join('another-dir', 'packages', 'package-2', 'file-3.css'))
atom.styles.addStyleSheet('atom-text-editor::shadow { color: gray }', sourcePath: path.join('another-dir', 'node_modules', 'package-3', 'file-4.css'))
promise = etch.getScheduler().getNextUpdatePromise()
waitsForPromise -> promise
runs ->
packageItems = deprecationCopView.element.querySelectorAll("ul.selectors > li")
expect(packageItems.length).toBe(3)
expect(packageItems[0].textContent).toMatch /package-1/
expect(packageItems[1].textContent).toMatch /package-2/
expect(packageItems[2].textContent).toMatch /Other/
packageDeprecationItems = packageItems[0].querySelectorAll("li.source-file")
expect(packageDeprecationItems.length).toBe(2)
expect(packageDeprecationItems[0].textContent).toMatch /atom-text-editor/
expect(packageDeprecationItems[0].querySelector("a").href).toMatch('some-dir/packages/package-1/file-1.css')
expect(packageDeprecationItems[1].textContent).toMatch /:host/
expect(packageDeprecationItems[1].querySelector("a").href).toMatch('some-dir/packages/package-1/file-2.css')
it 'skips stack entries which go through node_modules/ files when determining package name', ->
stack = [
{
"functionName": "function0"
"location": path.normalize "/Users/user/.atom/packages/package1/node_modules/atom-space-pen-viewslib/space-pen.js:55:66"
"fileName": path.normalize "/Users/user/.atom/packages/package1/node_modules/atom-space-pen-views/lib/space-pen.js"
}
{
"functionName": "function1"
"location": path.normalize "/Users/user/.atom/packages/package1/node_modules/atom-space-pen-viewslib/space-pen.js:15:16"
"fileName": path.normalize "/Users/user/.atom/packages/package1/node_modules/atom-space-pen-views/lib/space-pen.js"
}
{
"functionName": "function2"
"location": path.normalize "/Users/user/.atom/packages/package2/lib/module.js:13:14"
"fileName": path.normalize "/Users/user/.atom/packages/package2/lib/module.js"
}
]
packagePathsByPackageName = new Map([
['package1', path.normalize("/Users/user/.atom/packages/package1")],
['package2', path.normalize("/Users/user/.atom/packages/package2")]
])
spyOn(deprecationCopView, 'getPackagePathsByPackageName').andReturn(packagePathsByPackageName)
packageName = deprecationCopView.getPackageName(stack)
expect(packageName).toBe("package2")

View File

@@ -0,0 +1,72 @@
// The ui-variables file is provided by base themes provided by Atom.
//
// See https://github.com/atom/atom-dark-ui/blob/master/stylesheets/ui-variables.less
// for a full listing of what's available.
@import "ui-variables";
.deprecation-cop-status {
.icon.icon-alert:before {
// It's a really big icon...
width: 17px;
}
}
.deprecation-cop {
overflow: auto;
-webkit-flex: 1;
background-color: @app-background-color !important;
.deprecation-overview {
&:after {
content: '';
clear: both;
display: block;
}
}
.deprecation-info {
padding: 0 @component-padding;
}
.deprecation-info:hover {
background-color: @background-color-selected !important;
}
.deprecation-detail.list-item {
white-space: normal;
clear: both;
.deprecation-message {
padding: 5px 0 5px 28px;
line-height: 1.4;
font-size: @font-size;
p:last-child {
margin-bottom: 0;
}
}
.icon-alert {
margin-top: 5px;
float: left;
}
}
.collapsed > ul {
display: none;
}
.list {
list-style-type: none;
padding: 0;
}
.stack-trace {
background-color: @tool-panel-background-color;
padding: @component-padding;
margin-bottom: @component-padding;
}
a {
color: @text-color-highlight;
}
}