123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Sergey Melyukov @smelukov
- */
- "use strict";
- const { UsageState } = require("../ExportsInfo");
- /** @typedef {import("estree").Node} AnyNode */
- /** @typedef {import("../Dependency")} Dependency */
- /** @typedef {import("../ModuleGraph")} ModuleGraph */
- /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
- /** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
- /** @typedef {import("../Parser").ParserState} ParserState */
- /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
- /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
- /** @typedef {Map<TopLevelSymbol | null, Set<string | TopLevelSymbol> | true>} InnerGraph */
- /** @typedef {function(boolean | Set<string> | undefined): void} UsageCallback */
- /**
- * @typedef {Object} StateObject
- * @property {InnerGraph} innerGraph
- * @property {TopLevelSymbol=} currentTopLevelSymbol
- * @property {Map<TopLevelSymbol, Set<UsageCallback>>} usageCallbackMap
- */
- /** @typedef {false|StateObject} State */
- /** @type {WeakMap<ParserState, State>} */
- const parserStateMap = new WeakMap();
- const topLevelSymbolTag = Symbol("top level symbol");
- /**
- * @param {ParserState} parserState parser state
- * @returns {State} state
- */
- function getState(parserState) {
- return parserStateMap.get(parserState);
- }
- /**
- * @param {ParserState} parserState parser state
- * @returns {void}
- */
- exports.bailout = parserState => {
- parserStateMap.set(parserState, false);
- };
- /**
- * @param {ParserState} parserState parser state
- * @returns {void}
- */
- exports.enable = parserState => {
- const state = parserStateMap.get(parserState);
- if (state === false) {
- return;
- }
- parserStateMap.set(parserState, {
- innerGraph: new Map(),
- currentTopLevelSymbol: undefined,
- usageCallbackMap: new Map()
- });
- };
- /**
- * @param {ParserState} parserState parser state
- * @returns {boolean} true, when enabled
- */
- exports.isEnabled = parserState => {
- const state = parserStateMap.get(parserState);
- return !!state;
- };
- /**
- * @param {ParserState} state parser state
- * @param {TopLevelSymbol | null} symbol the symbol, or null for all symbols
- * @param {string | TopLevelSymbol | true} usage usage data
- * @returns {void}
- */
- exports.addUsage = (state, symbol, usage) => {
- const innerGraphState = getState(state);
- if (innerGraphState) {
- const { innerGraph } = innerGraphState;
- const info = innerGraph.get(symbol);
- if (usage === true) {
- innerGraph.set(symbol, true);
- } else if (info === undefined) {
- innerGraph.set(symbol, new Set([usage]));
- } else if (info !== true) {
- info.add(usage);
- }
- }
- };
- /**
- * @param {JavascriptParser} parser the parser
- * @param {string} name name of variable
- * @param {string | TopLevelSymbol | true} usage usage data
- * @returns {void}
- */
- exports.addVariableUsage = (parser, name, usage) => {
- const symbol =
- /** @type {TopLevelSymbol} */ (
- parser.getTagData(name, topLevelSymbolTag)
- ) || exports.tagTopLevelSymbol(parser, name);
- if (symbol) {
- exports.addUsage(parser.state, symbol, usage);
- }
- };
- /**
- * @param {ParserState} state parser state
- * @returns {void}
- */
- exports.inferDependencyUsage = state => {
- const innerGraphState = getState(state);
- if (!innerGraphState) {
- return;
- }
- const { innerGraph, usageCallbackMap } = innerGraphState;
- const processed = new Map();
- // flatten graph to terminal nodes (string, undefined or true)
- const nonTerminal = new Set(innerGraph.keys());
- while (nonTerminal.size > 0) {
- for (const key of nonTerminal) {
- /** @type {Set<string|TopLevelSymbol> | true} */
- let newSet = new Set();
- let isTerminal = true;
- const value = innerGraph.get(key);
- let alreadyProcessed = processed.get(key);
- if (alreadyProcessed === undefined) {
- alreadyProcessed = new Set();
- processed.set(key, alreadyProcessed);
- }
- if (value !== true && value !== undefined) {
- for (const item of value) {
- alreadyProcessed.add(item);
- }
- for (const item of value) {
- if (typeof item === "string") {
- newSet.add(item);
- } else {
- const itemValue = innerGraph.get(item);
- if (itemValue === true) {
- newSet = true;
- break;
- }
- if (itemValue !== undefined) {
- for (const i of itemValue) {
- if (i === key) continue;
- if (alreadyProcessed.has(i)) continue;
- newSet.add(i);
- if (typeof i !== "string") {
- isTerminal = false;
- }
- }
- }
- }
- }
- if (newSet === true) {
- innerGraph.set(key, true);
- } else if (newSet.size === 0) {
- innerGraph.set(key, undefined);
- } else {
- innerGraph.set(key, newSet);
- }
- }
- if (isTerminal) {
- nonTerminal.delete(key);
- // For the global key, merge with all other keys
- if (key === null) {
- const globalValue = innerGraph.get(null);
- if (globalValue) {
- for (const [key, value] of innerGraph) {
- if (key !== null && value !== true) {
- if (globalValue === true) {
- innerGraph.set(key, true);
- } else {
- const newSet = new Set(value);
- for (const item of globalValue) {
- newSet.add(item);
- }
- innerGraph.set(key, newSet);
- }
- }
- }
- }
- }
- }
- }
- }
- /** @type {Map<Dependency, true | Set<string>>} */
- for (const [symbol, callbacks] of usageCallbackMap) {
- const usage = /** @type {true | Set<string> | undefined} */ (
- innerGraph.get(symbol)
- );
- for (const callback of callbacks) {
- callback(usage === undefined ? false : usage);
- }
- }
- };
- /**
- * @param {ParserState} state parser state
- * @param {UsageCallback} onUsageCallback on usage callback
- */
- exports.onUsage = (state, onUsageCallback) => {
- const innerGraphState = getState(state);
- if (innerGraphState) {
- const { usageCallbackMap, currentTopLevelSymbol } = innerGraphState;
- if (currentTopLevelSymbol) {
- let callbacks = usageCallbackMap.get(currentTopLevelSymbol);
- if (callbacks === undefined) {
- callbacks = new Set();
- usageCallbackMap.set(currentTopLevelSymbol, callbacks);
- }
- callbacks.add(onUsageCallback);
- } else {
- onUsageCallback(true);
- }
- } else {
- onUsageCallback(undefined);
- }
- };
- /**
- * @param {ParserState} state parser state
- * @param {TopLevelSymbol} symbol the symbol
- */
- exports.setTopLevelSymbol = (state, symbol) => {
- const innerGraphState = getState(state);
- if (innerGraphState) {
- innerGraphState.currentTopLevelSymbol = symbol;
- }
- };
- /**
- * @param {ParserState} state parser state
- * @returns {TopLevelSymbol|void} usage data
- */
- exports.getTopLevelSymbol = state => {
- const innerGraphState = getState(state);
- if (innerGraphState) {
- return innerGraphState.currentTopLevelSymbol;
- }
- };
- /**
- * @param {JavascriptParser} parser parser
- * @param {string} name name of variable
- * @returns {TopLevelSymbol} symbol
- */
- exports.tagTopLevelSymbol = (parser, name) => {
- const innerGraphState = getState(parser.state);
- if (!innerGraphState) return;
- parser.defineVariable(name);
- const existingTag = /** @type {TopLevelSymbol} */ (
- parser.getTagData(name, topLevelSymbolTag)
- );
- if (existingTag) {
- return existingTag;
- }
- const fn = new TopLevelSymbol(name);
- parser.tagVariable(name, topLevelSymbolTag, fn);
- return fn;
- };
- /**
- * @param {Dependency} dependency the dependency
- * @param {Set<string> | boolean} usedByExports usedByExports info
- * @param {ModuleGraph} moduleGraph moduleGraph
- * @param {RuntimeSpec} runtime runtime
- * @returns {boolean} false, when unused. Otherwise true
- */
- exports.isDependencyUsedByExports = (
- dependency,
- usedByExports,
- moduleGraph,
- runtime
- ) => {
- if (usedByExports === false) return false;
- if (usedByExports !== true && usedByExports !== undefined) {
- const selfModule = moduleGraph.getParentModule(dependency);
- const exportsInfo = moduleGraph.getExportsInfo(selfModule);
- let used = false;
- for (const exportName of usedByExports) {
- if (exportsInfo.getUsed(exportName, runtime) !== UsageState.Unused)
- used = true;
- }
- if (!used) return false;
- }
- return true;
- };
- /**
- * @param {Dependency} dependency the dependency
- * @param {Set<string> | boolean} usedByExports usedByExports info
- * @param {ModuleGraph} moduleGraph moduleGraph
- * @returns {null | false | function(ModuleGraphConnection, RuntimeSpec): ConnectionState} function to determine if the connection is active
- */
- exports.getDependencyUsedByExportsCondition = (
- dependency,
- usedByExports,
- moduleGraph
- ) => {
- if (usedByExports === false) return false;
- if (usedByExports !== true && usedByExports !== undefined) {
- const selfModule = moduleGraph.getParentModule(dependency);
- const exportsInfo = moduleGraph.getExportsInfo(selfModule);
- return (connections, runtime) => {
- for (const exportName of usedByExports) {
- if (exportsInfo.getUsed(exportName, runtime) !== UsageState.Unused)
- return true;
- }
- return false;
- };
- }
- return null;
- };
- class TopLevelSymbol {
- /**
- * @param {string} name name of the variable
- */
- constructor(name) {
- this.name = name;
- }
- }
- exports.TopLevelSymbol = TopLevelSymbol;
- exports.topLevelSymbolTag = topLevelSymbolTag;
|