All files / dist/helpers exports.js

40% Statements 26/65
18.87% Branches 10/53
62.5% Functions 5/8
46.15% Lines 24/52
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144    1x     1x 1x   1x   1x   1x   1x   3x                                   1x   1x             3x               1x 1x   1x                 1x   1x 3x             1x               1x       1x   1x     1x 1x                                       1x                                                                
"use strict";
 
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.collapseNamedExports = collapseNamedExports;
exports.getDefaultExportName = getDefaultExportName;
 
require("source-map-support/register");
 
var t = _interopRequireWildcard(require("@babel/types"));
 
var th = _interopRequireWildcard(require("./templates"));
 
var ast = _interopRequireWildcard(require("./ast"));
 
function _interopRequireWildcard(obj) { Eif (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
 
/**
 * Collapse named exports onto the default export so that the default export can be returned directly,
 * so that exports can be easily used by code that does not include an import interop.
 * This includes the following:
 *  - Ignore named exports that already exist on the default export and which reference the same identifer.
 *  - If possible, assign any remaining exports to the default export.
 *    - This cannot be done if there is a naming conflict.
 *
 * In order to determine what properties the default export already has, the plugin will scan the
 * top level of the program to find any assignments, including Object.assign() and _extend() calls.
 * It does this recursively in the case of those method calls.
 *
 * If there are any named exports left, they get returned, so the main plugin can check the opts
 * and decide if an error should be thrown.
 */
function collapseNamedExports(programNode, defaultExport, namedExports, opts) {
  namedExports = filterOutExportsWhichAlreadyMatchPropsOnDefault(programNode, defaultExport, namedExports);
 
  Iif (!namedExports.length || opts.noExportExtend) {
    return {
      filteredExports: namedExports,
      newDefaultExportIdentifier: null
    };
  }
 
  Iif (namedExports.some(exp => exp.conflict)) {
    return {
      filteredExports: namedExports,
      conflictingExports: namedExports.filter(exp => exp.conflict),
      newDefaultExportIdentifier: null
    };
  }
 
  let exportVariableName = getDefaultExportName(defaultExport);
  let newDefaultExportIdentifier = null;
 
  Iif (!exportVariableName) {
    // Something anonymous (literal, anon function, etc...)
    programNode.body.push(th.buildTempExport({
      VALUE: defaultExport
    }));
    newDefaultExportIdentifier = th.exportsIdentifier;
    exportVariableName = newDefaultExportIdentifier.name;
  }
 
  const id = t.identifier(exportVariableName);
 
  for (const namedExport of namedExports) {
    programNode.body.push(th.buildAssign({
      OBJECT: id,
      NAME: namedExport.key,
      VALUE: namedExport.value
    }));
  }
 
  return {
    filteredExports: [],
    conflictingExports: [],
    newDefaultExportIdentifier: newDefaultExportIdentifier
  };
}
 
function getDefaultExportName(defaultExport) {
  return defaultExport.name || ast.getIdName(defaultExport);
}
 
function filterOutExportsWhichAlreadyMatchPropsOnDefault(programNode, defaultExport, namedExports) {
  namedExports = [...namedExports]; // Shallow clone for safe splicing
 
  const defaultObjectProperties = ast.findPropertiesOfNode(programNode, defaultExport); // Loop through the default object's props and see if it already has a matching definition of the named exports.
  // If the definition matches, we can ignore the named export. If the definition does not match, mark it as a conflict.
 
  Eif (defaultObjectProperties) {
    for (const defaultExportProperty of defaultObjectProperties) {
      if (!defaultExportProperty.key) {
        // i.e. SpreadProperty
        continue;
      }
 
      const matchingNamedExportIndex = namedExports.findIndex(namedExport => namedExport.key.name === defaultExportProperty.key.name); // get the index for splicing
 
      const matchingNamedExport = namedExports[matchingNamedExportIndex];
 
      if (matchingNamedExport && !matchingNamedExport.conflict) {
        if (areSame(defaultExportProperty, matchingNamedExport)) {
          namedExports.splice(matchingNamedExportIndex, 1);
        } else {
          matchingNamedExport.conflict = true;
        }
      }
    }
  }
 
  return namedExports;
}
 
function areSame(defaultExportProperty, matchingNamedExport) {
  const defaultExportValue = defaultExportProperty.value;
  const declaration = matchingNamedExport.declaration; // i.e. 'export const a = 1' or 'export let a = b'
 
  if (!defaultExportValue) {
    // i.e. object method
    return false; // always a conflict
  } else if (t.isIdentifier(defaultExportValue)) {
    const valueName = defaultExportValue.name;
 
    if (valueName === matchingNamedExport.value.name) {
      // Same identifier reference. Safe to ignore
      return true;
    } else if (declaration && t.isIdentifier(declaration.init) && declaration.init.name === valueName) {
      // i.e. 'export let a = b', and default value for a is 'b'.
      return true;
    }
  } else if (t.isLiteral(defaultExportValue)) {
    // i.e. { a: 1 } or { a: '1' }
    // See if the original declaration for the matching export was for the same literal
    if (t.isLiteral(declaration.init) && declaration.init.value === defaultExportValue.value) {
      // i.e. 'export const a = 1'
      // Same literal. Safe to ignore
      return true;
    }
  } // Either a conflicting value or no value at all (i.e. method)
 
 
  return false;
}