vuese / vuese

🤗 One-stop solution for vue component documentation. Original org: https://github.com/vuese
https://vuese.github.io/website/
MIT License
1.7k stars 119 forks source link

Parser result contains slots more than once if present in template + script #83

Closed ChristianKienle closed 5 years ago

ChristianKienle commented 5 years ago

Describe the bug If the template contains a named slot <slot name="back" /> is will be part of the slot-parser result. That is fine. However, if your script contains $slots.back it will appear twice.

To Reproduce Steps to reproduce the behavior: Paste the following snippet into the explorer:

<template>
  <div>
    <slot name="back" />
  </div>
</template>

<script>
export default {
  name: 'MyComponent',
 computed: {
    hasBack() {
      return this.$slots.back != null;
    }
  }
}
</script>

Expected behavior I expect the slots-array to only contain a single entry.

Which version do you use: latest

Additional information FYI: I have noticed that the unit tests don't fail because the count is not checked...

Btw: Fundamental Vue is adopting Vuese right now. :) We will slowly transition to Vuese in order to make our manually managed API-documentation obsolete.

ChristianKienle commented 5 years ago

This is the (untested) workaround we are currently using:


/**
 * For context see below.
 * @param {import("@vuese/parser").SlotResult} lhsSlot
 * @param {import("@vuese/parser").SlotResult} rhsSlot
 * @returns {import("@vuese/parser").SlotResult}
 */
const mergedSlot = (lhsSlot, rhsSlot) => {
  const name = lhsSlot.name;
  const { describe: lhsDescribe, backerDesc: lhsBackerDesc, bindings: lhsBindings, scoped: lhsScoped  } = lhsSlot;
  const { describe: rhsDescribe, backerDesc: rhsBackerDesc, bindings: rhsBindings, scoped: rhsScoped  } = rhsSlot;
  const describe = lhsDescribe.length >= rhsDescribe.length ? lhsDescribe : rhsDescribe;
  const backerDesc = lhsBackerDesc.length >= rhsBackerDesc.length ? lhsBackerDesc : rhsBackerDesc;
  const bindings = Object.keys(lhsBindings).length >= Object.keys(rhsBindings).length ? lhsBindings : rhsBindings;
  // The default seems to be 'false' – so if lhsScoped is true we take that. Otherwise we simply use rhsScoped
  const scoped = lhsScoped === true ? true : rhsScoped;
  return { scoped, name, describe, backerDesc, bindings };
};

/**
 * For context see below.
 * @param {import("@vuese/parser").SlotResult} slot
 * @param {import("@vuese/parser").SlotResult[]} otherSlots
 * @returns {import("@vuese/parser").SlotResult}
 */
const fixedSlot = (slot, otherSlots) => {
  const duplicateSlots = otherSlots.filter(({ name }) => name === slot.name);
  if(duplicateSlots.length === 0) {
    return slot; // no duplicates – phew
  }
  let fixed = slot;
  duplicateSlots.forEach(otherSlot => {
    fixed = mergedSlot(fixed, otherSlot);
  });
  return fixed;
};

/**
 * For context see fixedParserResult below.
 * @param {import("@vuese/parser").SlotResult[]} slots
 * @returns {import("@vuese/parser").SlotResult[]}
 */
const fixedParserSlots = slots => {
  const fixedButDuplicated = slots.map((slot, index) => {
    const otherSlots = [...slots];
    // remove the current slot from the copy
    // the result are the 'other slots'
    otherSlots.splice(index, 1);
    return fixedSlot(slot, otherSlots);
  });
  // fixedButDuplicated now contains correct slots but duplicated still remain.
  const result = [];
  fixedButDuplicated.forEach(slot => {
    const alreadyInResult = result.findIndex(resultSlot => resultSlot.name === slot.name) >= 0;
    if(alreadyInResult) {
      return;
    }
    result.push(slot);
  });
  return result;
};

/**
 * Fixes the parser result. There is currently a bug in @vuese/parser
 * which causes result.slots to contain duplicate entries.
 * See: https://github.com/vuese/vuese/issues/83
 * @param {import("@vuese/parser").ParserResult} result
 * @returns {import("@vuese/parser").ParserResult}
 */
const fixedParserResult = result => {
  const { slots = [] } = result;
  return { ...result, slots: fixedParserSlots(slots) };
};
HcySunYang commented 5 years ago

Just use:

ChristianKienle commented 5 years ago

Thank you very much. You are awesome. :)