simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

HarmonyExportInitFragment.js (4529B)


      1 /*
      2 	MIT License http://www.opensource.org/licenses/mit-license.php
      3 	Author Tobias Koppers @sokra
      4 */
      5 
      6 "use strict";
      7 
      8 const InitFragment = require("../InitFragment");
      9 const RuntimeGlobals = require("../RuntimeGlobals");
     10 const { first } = require("../util/SetHelpers");
     11 
     12 /** @typedef {import("webpack-sources").Source} Source */
     13 /** @typedef {import("../Generator").GenerateContext} GenerateContext */
     14 
     15 const joinIterableWithComma = iterable => {
     16 	// This is more performant than Array.from().join(", ")
     17 	// as it doesn't create an array
     18 	let str = "";
     19 	let first = true;
     20 	for (const item of iterable) {
     21 		if (first) {
     22 			first = false;
     23 		} else {
     24 			str += ", ";
     25 		}
     26 		str += item;
     27 	}
     28 	return str;
     29 };
     30 
     31 const EMPTY_MAP = new Map();
     32 const EMPTY_SET = new Set();
     33 
     34 /**
     35  * @typedef {GenerateContext} Context
     36  */
     37 class HarmonyExportInitFragment extends InitFragment {
     38 	/**
     39 	 * @param {string} exportsArgument the exports identifier
     40 	 * @param {Map<string, string>} exportMap mapping from used name to exposed variable name
     41 	 * @param {Set<string>} unusedExports list of unused export names
     42 	 */
     43 	constructor(
     44 		exportsArgument,
     45 		exportMap = EMPTY_MAP,
     46 		unusedExports = EMPTY_SET
     47 	) {
     48 		super(undefined, InitFragment.STAGE_HARMONY_EXPORTS, 1, "harmony-exports");
     49 		this.exportsArgument = exportsArgument;
     50 		this.exportMap = exportMap;
     51 		this.unusedExports = unusedExports;
     52 	}
     53 
     54 	/**
     55 	 * @param {HarmonyExportInitFragment[]} fragments all fragments to merge
     56 	 * @returns {HarmonyExportInitFragment} merged fragment
     57 	 */
     58 	mergeAll(fragments) {
     59 		let exportMap;
     60 		let exportMapOwned = false;
     61 		let unusedExports;
     62 		let unusedExportsOwned = false;
     63 
     64 		for (const fragment of fragments) {
     65 			if (fragment.exportMap.size !== 0) {
     66 				if (exportMap === undefined) {
     67 					exportMap = fragment.exportMap;
     68 					exportMapOwned = false;
     69 				} else {
     70 					if (!exportMapOwned) {
     71 						exportMap = new Map(exportMap);
     72 						exportMapOwned = true;
     73 					}
     74 					for (const [key, value] of fragment.exportMap) {
     75 						if (!exportMap.has(key)) exportMap.set(key, value);
     76 					}
     77 				}
     78 			}
     79 			if (fragment.unusedExports.size !== 0) {
     80 				if (unusedExports === undefined) {
     81 					unusedExports = fragment.unusedExports;
     82 					unusedExportsOwned = false;
     83 				} else {
     84 					if (!unusedExportsOwned) {
     85 						unusedExports = new Set(unusedExports);
     86 						unusedExportsOwned = true;
     87 					}
     88 					for (const value of fragment.unusedExports) {
     89 						unusedExports.add(value);
     90 					}
     91 				}
     92 			}
     93 		}
     94 		return new HarmonyExportInitFragment(
     95 			this.exportsArgument,
     96 			exportMap,
     97 			unusedExports
     98 		);
     99 	}
    100 
    101 	merge(other) {
    102 		let exportMap;
    103 		if (this.exportMap.size === 0) {
    104 			exportMap = other.exportMap;
    105 		} else if (other.exportMap.size === 0) {
    106 			exportMap = this.exportMap;
    107 		} else {
    108 			exportMap = new Map(other.exportMap);
    109 			for (const [key, value] of this.exportMap) {
    110 				if (!exportMap.has(key)) exportMap.set(key, value);
    111 			}
    112 		}
    113 		let unusedExports;
    114 		if (this.unusedExports.size === 0) {
    115 			unusedExports = other.unusedExports;
    116 		} else if (other.unusedExports.size === 0) {
    117 			unusedExports = this.unusedExports;
    118 		} else {
    119 			unusedExports = new Set(other.unusedExports);
    120 			for (const value of this.unusedExports) {
    121 				unusedExports.add(value);
    122 			}
    123 		}
    124 		return new HarmonyExportInitFragment(
    125 			this.exportsArgument,
    126 			exportMap,
    127 			unusedExports
    128 		);
    129 	}
    130 
    131 	/**
    132 	 * @param {Context} context context
    133 	 * @returns {string|Source} the source code that will be included as initialization code
    134 	 */
    135 	getContent({ runtimeTemplate, runtimeRequirements }) {
    136 		runtimeRequirements.add(RuntimeGlobals.exports);
    137 		runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
    138 
    139 		const unusedPart =
    140 			this.unusedExports.size > 1
    141 				? `/* unused harmony exports ${joinIterableWithComma(
    142 						this.unusedExports
    143 				  )} */\n`
    144 				: this.unusedExports.size > 0
    145 				? `/* unused harmony export ${first(this.unusedExports)} */\n`
    146 				: "";
    147 		const definitions = [];
    148 		const orderedExportMap = Array.from(this.exportMap).sort(([a], [b]) =>
    149 			a < b ? -1 : 1
    150 		);
    151 		for (const [key, value] of orderedExportMap) {
    152 			definitions.push(
    153 				`\n/* harmony export */   ${JSON.stringify(
    154 					key
    155 				)}: ${runtimeTemplate.returningFunction(value)}`
    156 			);
    157 		}
    158 		const definePart =
    159 			this.exportMap.size > 0
    160 				? `/* harmony export */ ${RuntimeGlobals.definePropertyGetters}(${
    161 						this.exportsArgument
    162 				  }, {${definitions.join(",")}\n/* harmony export */ });\n`
    163 				: "";
    164 		return `${definePart}${unusedPart}`;
    165 	}
    166 }
    167 
    168 module.exports = HarmonyExportInitFragment;