Transform deprecated shadow DOM selectors in StyleManager

This commit is contained in:
Antonio Scandurra
2016-10-04 11:22:41 +02:00
parent 92a3c2f4b2
commit 80fc448b8d
8 changed files with 133 additions and 152 deletions

View File

@@ -863,7 +863,7 @@ module.exports = new Set([
'trace', 'trace-argument', 'trace-object', 'traceback', 'tracing',
'track_processing', 'trader', 'tradersk', 'trail', 'trailing',
'trailing-array-separator', 'trailing-dictionary-separator', 'trailing-match',
'trailing-whitespace', 'trait', 'traits', 'traits-keyword', 'transaction',
'trait', 'traits', 'traits-keyword', 'transaction',
'transcendental', 'transcludeblock', 'transcludeinline', 'transclusion',
'transform', 'transformation', 'transient', 'transition',
'transitionable-property-value', 'translation', 'transmission-filter',

View File

@@ -202,8 +202,8 @@ class LinesTileComponent
if @presenter.isCloseTagCode(tagCode)
openScopeNode = openScopeNode.parentElement
else if @presenter.isOpenTagCode(tagCode)
scopes = @presenter.tagForCode(tagCode).replace(/\s+/g, '.').split('.').map((scope) -> "syntax--#{scope}")
newScopeNode = @domElementPool.buildElement("span", scopes.join(' '))
scope = @presenter.tagForCode(tagCode)
newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' '))
openScopeNode.appendChild(newScopeNode)
openScopeNode = newScopeNode
else
@@ -219,7 +219,7 @@ class LinesTileComponent
if lineText.endsWith(@presenter.displayLayer.foldCharacter)
# Insert a zero-width non-breaking whitespace, so that
# LinesYardstick can take the syntax--fold-marker::after pseudo-element
# LinesYardstick can take the fold-marker::after pseudo-element
# into account during measurements when such marker is the last
# character on the line.
textNode = @domElementPool.buildText(ZERO_WIDTH_NBSP)

View File

@@ -1,7 +1,10 @@
const {Emitter, Disposable} = require('event-kit')
const fs = require('fs-plus')
const path = require('path')
const {Emitter, Disposable} = require('event-kit')
const postcss = require('postcss')
const selectorParser = require('postcss-selector-parser')
const StylesElement = require('./styles-element')
const DEPRECATED_SYNTAX_SELECTORS = require('./deprecated-syntax-selectors')
// Extended: A singleton instance of this class available via `atom.styles`,
// which you can use to globally query and observe the set of active style
@@ -122,7 +125,7 @@ module.exports = class StyleManager {
}
}
styleElement.textContent = source
styleElement.textContent = transformDeprecatedShadowDOMSelectors(source, params.context)
if (updated) {
this.emitter.emit('did-update-style-element', styleElement)
} else {
@@ -205,3 +208,37 @@ module.exports = class StyleManager {
}
}
}
function transformDeprecatedShadowDOMSelectors (css, context) {
const root = postcss.parse(css)
root.walkRules((rule) => {
rule.selector = selectorParser((selectors) => {
selectors.each((selector) => {
const firstNode = selector.nodes[0]
if (context === 'atom-text-editor' && firstNode.type === 'pseudo' && firstNode.value === ':host') {
const atomTextEditorElementNode = selectorParser.tag({value: 'atom-text-editor'})
firstNode.replaceWith(atomTextEditorElementNode)
}
let targetsAtomTextEditorShadow = context === 'atom-text-editor'
let previousNode
selector.each((node) => {
if (targetsAtomTextEditorShadow && node.type === 'class') {
if (DEPRECATED_SYNTAX_SELECTORS.has(node.value) && !node.value.startsWith('syntax--')) {
node.value = `syntax--${node.value}`
}
} else if (previousNode) {
const currentNodeIsShadowPseudoClass = node.type === 'pseudo' && node.value === '::shadow'
const previousNodeIsAtomTextEditor = previousNode.type === 'tag' && previousNode.value === 'atom-text-editor'
if (previousNodeIsAtomTextEditor && currentNodeIsShadowPseudoClass) {
selector.removeChild(node)
targetsAtomTextEditorShadow = true
}
}
previousNode = node
})
})
}).process(rule.selector).result
})
return root.toString()
}

View File

@@ -1,9 +1,4 @@
{Emitter, CompositeDisposable} = require 'event-kit'
selectorProcessor = require 'postcss-selector-parser'
SPATIAL_DECORATIONS = new Set([
'invisible-character', 'hard-tab', 'leading-whitespace',
'trailing-whitespace', 'eol', 'indent-guide', 'fold-marker'
])
class StylesElement extends HTMLElement
subscriptions: null
@@ -24,9 +19,6 @@ class StylesElement extends HTMLElement
@styleElementClonesByOriginalElement = new WeakMap
attachedCallback: ->
for styleElement in @children
@upgradeDeprecatedSelectors(styleElement)
@context = @getAttribute('context') ? undefined
detachedCallback: ->
@@ -68,7 +60,6 @@ class StylesElement extends HTMLElement
break
@insertBefore(styleElementClone, insertBefore)
@upgradeDeprecatedSelectors(styleElementClone)
@emitter.emit 'did-add-style-element', styleElementClone
styleElementRemoved: (styleElement) ->
@@ -88,52 +79,4 @@ class StylesElement extends HTMLElement
styleElementMatchesContext: (styleElement) ->
not @context? or styleElement.context is @context
upgradeDeprecatedSelectors: (styleElement) ->
return unless styleElement.sheet?
transformDeprecatedShadowSelectors = (selectors) ->
selectors.each (selector) ->
isSyntaxSelector = not selector.some((node) ->
(node.type is 'tag' and node.value is 'atom-text-editor') or
(node.type is 'class' and node.value is 'region') or
(node.type is 'class' and node.value is 'wrap-guide') or
(node.type is 'class' and /spell-check/.test(node.value))
)
previousNode = null
selector.each (node) ->
isShadowPseudoClass = node.type is 'pseudo' and node.value is '::shadow'
isHostPseudoClass = node.type is 'pseudo' and node.value is ':host'
if isHostPseudoClass and not previousNode?
newNode = selectorProcessor.tag({value: 'atom-text-editor'})
node.replaceWith(newNode)
previousNode = newNode
else if isShadowPseudoClass and previousNode?.type is 'tag' and previousNode?.value is 'atom-text-editor'
selector.removeChild(node)
else
if styleElement.context is 'atom-text-editor' and node.type is 'class'
if (isSyntaxSelector and not node.value.startsWith('syntax--')) or SPATIAL_DECORATIONS.has(node.value)
node.value = 'syntax--' + node.value
previousNode = node
upgradedSelectors = []
for rule in styleElement.sheet.cssRules when rule.selectorText?
inputSelector = rule.selectorText
outputSelector = rule.selectorText
outputSelector = selectorProcessor(transformDeprecatedShadowSelectors).process(outputSelector).result
if inputSelector isnt outputSelector
rule.selectorText = outputSelector
upgradedSelectors.push({inputSelector, outputSelector})
if upgradedSelectors.length > 0
upgradedSelectorsText = upgradedSelectors.map(({inputSelector, outputSelector}) -> "`#{inputSelector}` => `#{outputSelector}`").join('\n')
console.warn("""
Shadow DOM for `atom-text-editor` elements has been removed. This means
should stop using :host and ::shadow pseudo-selectors, and prepend all
your syntax selectors with `syntax--`. To prevent breakage with existing
stylesheets, we have automatically upgraded the following selectors in
`#{styleElement.sourcePath}`:
#{upgradedSelectorsText}
""")
module.exports = StylesElement = document.registerElement 'atom-styles', prototype: StylesElement.prototype