docs: function parseProperties(source, baseDirectory) {
    // Get the raw SCSS concrete syntax tree.
    const rawTree = parseCSS(source);
    removeWhitespace(rawTree);
  
    // Convert the concrete syntax tree into a more convenient AST.
    let schema
    const rootRules = []
    for (const rule of rawTree.value) {
      if (rule.type === 'atrule') {
        removeWhitespace(rule);
        const [keyword, argument] = rule.value;
        if (keyword.value === 'schema') {
          schema = visitSchema(argument, baseDirectory)
        } else if (keyword.value === 'import') {
          rootRules.push(...visitImport(argument, baseDirectory))
        }
      } else {
        rootRules.push(visitRule(rule, false))
      }
    }
  
    // Flatten out any nested selectors.
    const rules = []
    for (const rootRule of rootRules) {
      flattenRule(rootRule, rules, [[]]);
    }
  
    const validator = new Validator();
  
    for (const rule of rules) {
      // Validate each rule's properties against the schema.
      for (const property in rule.properties) {
        if (schema) {
          const propertySchema = schema[property]
          const value = rule.properties[property]
          if (!propertySchema) {
            throw new Error(`Property '${property}' is not present in the schema`);
          }
          const {errors} = validator.validate(value, propertySchema);
          if (errors.length > 0) {
            throw new Error(`Invalid value '${value}' for property '${property}'`)
          }
        }
      }
  
      rule.selectors = rule.selectors.map(applyPseudoClasses)
    }
  
    return rules;
  }