8000 Syntaxes like `postcss-html` adjust node source locations relative to enclosing non-css documents · Issue #1995 · postcss/postcss · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Syntaxes like postcss-html adjust node source locations relative to enclosing non-css documents #1995
Closed
@romainmenke

Description

@romainmenke

See: https://github.com/ota-meshi/postcss-html/blob/168b49076ad66fe0e8766cb6ee899fa1534d3680/lib/html/parse-styles.js#L55-L76

For example:

<p>Hello</p>

<style>
p {
  color: green;
}
</style>

The p {} rule when parsed with postcss-html has source locations that are offset relative to the entire html doc.

So p {} starts on line 4, not on line 1.

This is very useful when reporting errors (i.e. Stylelint) to developers in a code editors.
As the error will be reported on the line and column in the entire document.


However this unfortunately conflicts with the changes I introduced in #1980

Since those changes, we use node.source.input.css to infer positions.
But we assume the start of node.source.input.css corresponds with:

  • index == 0
  • line == 1 and column == 1

Because postcss-html offsets the positions relative to the enclosing document this assumptions proves to be incorrect, making any position calculations incorrect.


The simplest way forward I could think of was to add an extra field on Input to keep track of both the source of the enclosing document and of the CSS block independently.

postcss/lib/input.js

Lines 17 to 34 in 99da2f2

class Input {
constructor(css, opts = {}) {
if (
css === null ||
typeof css === 'undefined' ||
(typeof css === 'object' && !css.toString)
) {
throw new Error(`PostCSS received ${css} instead of CSS string`)
}
this.css = css.toString()
if (this.css[0] === '\uFEFF' || this.css[0] === '\uFFFE') {
this.hasBOM = true
this.css = this.css.slice(1)
} else {
this.hasBOM = false
}

class Input {
  constructor(css, opts = {}) {
    if (
      css === null ||
      typeof css === 'undefined' ||
      (typeof css === 'object' && !css.toString)
    ) {
      throw new Error(`PostCSS received ${css} instead of CSS string`)
    }

    this.css = css.toString()

    if (this.css[0] === '\uFEFF' || this.css[0] === '\uFFFE') {
      this.hasBOM = true
      this.css = this.css.slice(1)
    } else {
      this.hasBOM = false
    }

    this.document = this.css
    if (opts.document) this.document = opts.document.toString()

    // ...

For almost all usage of PostCSS input.document would correspond to input.css as the document is the CSS stylesheet.

But for CSS-in-X syntaxes (like postcss-html) the syntax authors could set document to the enclosing source (e.g. the html document).


When inferring positions we would use node.source.input.document instead of node.source.input.css in PostCSS itself.

  positionBy(opts) {
    let pos = this.source.start
    if (opts.index) {
      pos = this.positionInside(opts.index)
    } else if (opts.word) {
      let stringRepresentation = this.source.input.document.slice(
        sourceOffset(this.source.input.document, this.source.start),
        sourceOffset(this.source.input.document, this.source.end)
      )
      let index = stringRepresentation.indexOf(opts.word)
      if (index !== -1) pos = this.positionInside(index)
    }
    return pos
  }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0