postcss_import_hoisting.js

/** @module */
import postcssPlugin from '../../helpers/postcss_plugin'

/**
 * Marpit PostCSS import hoisting plugin.
 *
 * Hoist `@charset` and `@import` at-rule to the beginning of CSS. Marpit is
 * manipulating CSS with many PostCSS plugins, so sometimes a few at-rules
 * cannot keep specification.
 *
 * This plugin takes care of hoisting for invalid at-rules.
 *
 * @function importHoisting
 */
export const importHoisting = postcssPlugin(
  'marpit-postcss-import-hoisting',
  () => (css) => {
    const hoisted = {
      charset: undefined,
      imports: [],
    }

    css.walkAtRules((rule) => {
      if (rule.name === 'charset') {
        rule.remove()
        if (!hoisted.charset) hoisted.charset = rule
      } else if (rule.name === 'import') {
        hoisted.imports.push(rule.remove())
      }
    })

    const { first } = css

    // Hoist at-rules
    ;[hoisted.charset, ...hoisted.imports]
      .filter((r) => r)
      .forEach((rule, idx) => {
        // Strip whitespace from the beginning of first at-rule
        const prependRule =
          idx === 0 ? rule.clone({ raws: { before: undefined } }) : rule

        first.before(prependRule)
      })
  },
)

export default importHoisting