Don't transform deprecated selectors containing malformed CSS

This commit is contained in:
Antonio Scandurra
2017-01-20 15:09:57 +01:00
parent 62d804b858
commit 93f2e34ded
2 changed files with 72 additions and 54 deletions

View File

@@ -98,6 +98,13 @@ describe('StyleManager', () => {
])
})
it('does not transform CSS rules with invalid syntax', () => {
styleManager.addStyleSheet("atom-text-editor::shadow .class-1 { font-family: inval'id }")
expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([
'atom-text-editor::shadow .class-1'
])
})
it('does not throw exceptions on rules with no selectors', () => {
styleManager.addStyleSheet('@media screen {font-size: 10px}', {context: 'atom-text-editor'})
})

View File

@@ -250,59 +250,70 @@ module.exports = class StyleManager {
function transformDeprecatedShadowDOMSelectors (css, context) {
const transformedSelectors = []
const transformedSource = postcss.parse(css)
transformedSource.walkRules((rule) => {
const transformedSelector = 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 previousNodeIsAtomTextEditor = false
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 = `syntax--${node.value}`
}
} else {
if (previousNodeIsAtomTextEditor && node.type === 'pseudo' && node.value === '::shadow') {
node.type = 'className'
node.value = '.editor'
targetsAtomTextEditorShadow = true
}
}
previousNode = node
if (node.type === 'combinator') {
previousNodeIsAtomTextEditor = false
} else if (previousNode.type === 'tag' && previousNode.value === 'atom-text-editor') {
previousNodeIsAtomTextEditor = true
}
})
})
}).process(rule.selector, {lossless: true}).result
if (transformedSelector !== rule.selector) {
transformedSelectors.push({before: rule.selector, after: transformedSelector})
rule.selector = transformedSelector
}
})
let deprecationMessage
if (transformedSelectors.length > 0) {
deprecationMessage = 'Starting from Atom v1.13.0, the contents of `atom-text-editor` elements '
deprecationMessage += 'are no longer encapsulated within a shadow DOM boundary. '
deprecationMessage += 'This means you should stop using `:host` and `::shadow` '
deprecationMessage += 'pseudo-selectors, and prepend all your syntax selectors with `syntax--`. '
deprecationMessage += 'To prevent breakage with existing style sheets, Atom will automatically '
deprecationMessage += 'upgrade the following selectors:\n\n'
deprecationMessage += transformedSelectors
.map((selector) => `* \`${selector.before}\` => \`${selector.after}\``)
.join('\n\n') + '\n\n'
deprecationMessage += 'Automatic translation of selectors will be removed in a few release cycles to minimize startup time. '
deprecationMessage += 'Please, make sure to upgrade the above selectors as soon as possible.'
let transformedSource
try {
transformedSource = postcss.parse(css)
} catch (e) {
transformedSource = null
}
if (transformedSource) {
transformedSource.walkRules((rule) => {
const transformedSelector = 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 previousNodeIsAtomTextEditor = false
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 = `syntax--${node.value}`
}
} else {
if (previousNodeIsAtomTextEditor && node.type === 'pseudo' && node.value === '::shadow') {
node.type = 'className'
node.value = '.editor'
targetsAtomTextEditorShadow = true
}
}
previousNode = node
if (node.type === 'combinator') {
previousNodeIsAtomTextEditor = false
} else if (previousNode.type === 'tag' && previousNode.value === 'atom-text-editor') {
previousNodeIsAtomTextEditor = true
}
})
})
}).process(rule.selector, {lossless: true}).result
if (transformedSelector !== rule.selector) {
transformedSelectors.push({before: rule.selector, after: transformedSelector})
rule.selector = transformedSelector
}
})
let deprecationMessage
if (transformedSelectors.length > 0) {
deprecationMessage = 'Starting from Atom v1.13.0, the contents of `atom-text-editor` elements '
deprecationMessage += 'are no longer encapsulated within a shadow DOM boundary. '
deprecationMessage += 'This means you should stop using `:host` and `::shadow` '
deprecationMessage += 'pseudo-selectors, and prepend all your syntax selectors with `syntax--`. '
deprecationMessage += 'To prevent breakage with existing style sheets, Atom will automatically '
deprecationMessage += 'upgrade the following selectors:\n\n'
deprecationMessage += transformedSelectors
.map((selector) => `* \`${selector.before}\` => \`${selector.after}\``)
.join('\n\n') + '\n\n'
deprecationMessage += 'Automatic translation of selectors will be removed in a few release cycles to minimize startup time. '
deprecationMessage += 'Please, make sure to upgrade the above selectors as soon as possible.'
}
return {source: transformedSource.toString(), deprecationMessage}
} else {
// CSS was malformed so we don't transform it.
return {source: css}
}
return {source: transformedSource.toString(), deprecationMessage}
}