weapp-tailwindcss
Version:
把 tailwindcss 原子化样式思想,带给小程序开发者们! bring tailwindcss to miniprogram developers!
1,377 lines • 64.9 kB
JavaScript
import { n as _defineProperty, t as omitUndefined } from "./object-Bvy6-w9r.js";
import { n as HARD_PARSE_CACHE_MAX_ENTRIES, t as DEFAULT_PARSE_CACHE_MAX_SOURCE_LENGTH } from "./cache-options-DwBtLwQe.js";
import { splitCandidateTokens } from "@tailwindcss-mangle/engine";
import { LRUCache } from "lru-cache";
import { md5 as md5Hash } from "@weapp-tailwindcss/shared/node";
import { Parser } from "htmlparser2";
import _babelTraverse from "@babel/traverse";
import { parse, parseExpression } from "@babel/parser";
import MagicString from "magic-string";
import _createDebug from "debug";
import { MappingChars2String, escape } from "@weapp-core/escape";
import * as t from "@babel/types";
//#region src/babel/index.ts
function _interopDefaultCompat(e) {
return e && typeof e === "object" && "default" in e ? e.default : e;
}
const traverse = _interopDefaultCompat(_babelTraverse);
//#endregion
//#region src/utils/regex.ts
function escapeStringRegexp(str) {
if (typeof str !== "string") throw new TypeError("Expected a string");
return str.replaceAll(/[$()*+.?[\\\]^{|}]/g, "\\$&").replaceAll("-", "\\x2d");
}
//#endregion
//#region src/utils/nameMatcher.ts
const NEVER_MATCH_NAME$1 = () => false;
const GLOBAL_FLAG_REGEXP = /g/g;
function buildFuzzyMatcher(fuzzyStrings) {
if (fuzzyStrings.length === 0) return;
if (fuzzyStrings.length === 1) {
const [needle] = fuzzyStrings;
if (needle === void 0) return;
return (value) => value.includes(needle);
}
const unique = [...new Set(fuzzyStrings)];
const pattern = new RegExp(unique.map(escapeStringRegexp).join("|"));
return (value) => pattern.test(value);
}
function normaliseRegex(regex) {
const { source, flags } = regex;
if (!flags.includes("g")) return regex;
return new RegExp(source, flags.replace(GLOBAL_FLAG_REGEXP, ""));
}
function createNameMatcher(list, { exact = false } = {}) {
if (!list || list.length === 0) return NEVER_MATCH_NAME$1;
const exactStrings = exact ? /* @__PURE__ */ new Set() : void 0;
const fuzzyStrings = [];
const regexList = [];
for (const item of list) if (typeof item === "string") if (exact) exactStrings.add(item);
else fuzzyStrings.push(item);
else regexList.push(normaliseRegex(item));
if (exact) {
const exactStringCount = exactStrings?.size ?? 0;
if (exactStringCount === 1 && regexList.length === 0) {
const [needle] = exactStrings;
if (needle === void 0) return NEVER_MATCH_NAME$1;
return (value) => value === needle;
}
if (regexList.length === 0) return (value) => exactStrings.has(value);
if (exactStringCount === 0 && regexList.length === 1) {
const [regex] = regexList;
if (!regex) return NEVER_MATCH_NAME$1;
return (value) => regex.test(value);
}
return (value) => {
if (exactStrings?.has(value)) return true;
return regexList.some((regex) => regex.test(value));
};
}
const fuzzyMatcher = exact ? void 0 : buildFuzzyMatcher(fuzzyStrings);
const hasRegex = regexList.length > 0;
if (fuzzyMatcher && !hasRegex) return fuzzyMatcher;
if (!fuzzyMatcher && regexList.length === 1) {
const [regex] = regexList;
if (!regex) return NEVER_MATCH_NAME$1;
return (value) => regex.test(value);
}
return (value) => {
if (fuzzyMatcher?.(value)) return true;
if (!hasRegex) return false;
return regexList.some((regex) => regex.test(value));
};
}
//#endregion
//#region src/js/babel/parse.ts
const parseCache = new LRUCache({ max: HARD_PARSE_CACHE_MAX_ENTRIES });
function genCacheKey(source, options) {
if (typeof options === "string") return `${md5Hash(source)}:${options}`;
return `${md5Hash(source)}:${JSON.stringify(options, (_, val) => typeof val === "function" ? val.toString() : val)}`;
}
function normalizeCacheMaxEntries(value) {
if (typeof value !== "number" || !Number.isFinite(value)) return 128;
return Math.min(Math.max(Math.floor(value), 0), HARD_PARSE_CACHE_MAX_ENTRIES);
}
function normalizeCacheMaxSourceLength(value) {
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_PARSE_CACHE_MAX_SOURCE_LENGTH;
return Math.max(Math.floor(value), 0);
}
function trimParseCache(maxEntries) {
while (parseCache.size > maxEntries) parseCache.pop();
}
function babelParse(code, opts = {}) {
const { cache, cacheKey, cacheMaxEntries, cacheMaxSourceLength, ...rest } = opts;
const maxEntries = normalizeCacheMaxEntries(cacheMaxEntries);
const maxSourceLength = normalizeCacheMaxSourceLength(cacheMaxSourceLength);
const shouldCache = cache === true && maxEntries > 0 && code.length <= maxSourceLength;
const cacheKeyString = shouldCache ? genCacheKey(code, cacheKey ?? rest) : void 0;
let result;
if (shouldCache) {
trimParseCache(maxEntries);
result = parseCache.get(cacheKeyString);
}
if (!result) {
const { cache: _cache, cacheKey: _cacheKey, cacheMaxEntries: _cacheMaxEntries, cacheMaxSourceLength: _cacheMaxSourceLength, ...parseOptions } = opts;
result = parse(code, parseOptions);
if (shouldCache) {
parseCache.set(cacheKeyString, result);
trimParseCache(maxEntries);
}
}
return result;
}
//#endregion
//#region src/debug/index.ts
const _debug = _createDebug("weapp-tw");
function createDebug(prefix) {
const debug = ((formatter, ...args) => {
return _debug((prefix ?? "") + formatter, ...args);
});
Object.defineProperty(debug, "enabled", {
enumerable: false,
configurable: false,
get() {
return _debug.enabled;
}
});
return debug;
}
//#endregion
//#region src/wxml/shared.ts
const NEWLINE_RE = /[\n\r]+/g;
function replaceWxml(original, options = {
keepEOL: false,
escapeMap: MappingChars2String
}) {
const { keepEOL, escapeMap, ignoreHead } = options;
let res = original;
if (!keepEOL) res = res.replaceAll(NEWLINE_RE, "");
res = escape(res, omitUndefined({
map: escapeMap,
ignoreHead
}));
return res;
}
//#endregion
//#region src/shared/classname-transform.ts
const escapedCandidateCacheByEscapeMap = /* @__PURE__ */ new WeakMap();
const defaultEscapedCandidateCache = /* @__PURE__ */ new Map();
let lastEscapedCandidateEscapeMap;
let lastEscapedCandidateCacheStore;
function isUrlLikeCandidate(candidate) {
return candidate.startsWith("//") || candidate.startsWith("http://") || candidate.startsWith("https://");
}
function isPlainSlashPathCandidate(candidate) {
const slashIndex = candidate.indexOf("/");
if (slashIndex <= 0) return false;
if (isUrlLikeCandidate(candidate)) return true;
if (candidate.includes("[") || candidate.includes("]") || candidate.includes(":")) return false;
return !candidate.slice(0, slashIndex).includes("-");
}
function isArbitraryValueCandidate(candidate) {
let hasOpenBracket = false;
let hasCloseBracket = false;
for (let i = 0; i < candidate.length; i++) {
const char = candidate[i];
if (char === "[") hasOpenBracket = true;
else if (char === "]") hasCloseBracket = true;
if (hasOpenBracket && hasCloseBracket) break;
}
if (!hasOpenBracket || !hasCloseBracket) return false;
if (isUrlLikeCandidate(candidate.trim())) return false;
return true;
}
function shouldEnableArbitraryValueFallbackByInputs(classNameSet, jsArbitraryValueFallback, tailwindcssMajorVersion) {
if (jsArbitraryValueFallback === true) return true;
if (jsArbitraryValueFallback === false) return false;
return tailwindcssMajorVersion === 4 && (!classNameSet || classNameSet.size === 0);
}
function shouldEnableArbitraryValueFallback({ classNameSet, jsArbitraryValueFallback, tailwindcssMajorVersion }) {
return shouldEnableArbitraryValueFallbackByInputs(classNameSet, jsArbitraryValueFallback, tailwindcssMajorVersion);
}
const SKIP_RESULT = { decision: "skip" };
const DIRECT_RESULT = { decision: "direct" };
const FALLBACK_RESULT = { decision: "fallback" };
function getEscapedCandidateCacheStore(escapeMap) {
if (!escapeMap) return defaultEscapedCandidateCache;
if (escapeMap === lastEscapedCandidateEscapeMap && lastEscapedCandidateCacheStore) return lastEscapedCandidateCacheStore;
let store = escapedCandidateCacheByEscapeMap.get(escapeMap);
if (!store) {
store = /* @__PURE__ */ new Map();
escapedCandidateCacheByEscapeMap.set(escapeMap, store);
}
lastEscapedCandidateEscapeMap = escapeMap;
lastEscapedCandidateCacheStore = store;
return store;
}
function getEscapedCandidate(candidate, escapeMap, store = getEscapedCandidateCacheStore(escapeMap)) {
let cached = store.get(candidate);
if (cached === void 0) {
cached = replaceWxml(candidate, { escapeMap });
store.set(candidate, cached);
}
return cached;
}
/**
* JS 转译严格遵循 runtime class set:
* 1. 直接命中 classNameSet 原始值;
* 2. 兼容命中 classNameSet 中已转义值;
* 3. 仅在受控条件下允许 class 语义兜底。
*
* 返回结构化结果,附带已计算的 escapedValue 以避免下游重复 escape。
*/
function resolveClassNameTransformWithResult(candidate, { alwaysEscape, classNameSet, escapeMap, jsArbitraryValueFallback, jsPreserveClass, tailwindcssMajorVersion, classContext }) {
if (jsPreserveClass?.(candidate)) return SKIP_RESULT;
if (alwaysEscape) return DIRECT_RESULT;
if (!classContext && isPlainSlashPathCandidate(candidate)) return SKIP_RESULT;
if (classNameSet?.has(candidate)) return DIRECT_RESULT;
if (classNameSet && classNameSet.size > 0) {
const escapedCandidate = getEscapedCandidate(candidate, escapeMap);
if (escapedCandidate !== candidate && classNameSet.has(escapedCandidate)) return {
decision: "escaped",
escapedValue: escapedCandidate
};
}
if (classContext && shouldEnableArbitraryValueFallbackByInputs(classNameSet, jsArbitraryValueFallback, tailwindcssMajorVersion) && isArbitraryValueCandidate(candidate)) return FALLBACK_RESULT;
return SKIP_RESULT;
}
//#endregion
//#region src/utils/decode.ts
const unicodeEscapeRE = /\\u([\dA-Fa-f]{4})/g;
const unicodeEscapeTestRE = /\\u[\dA-Fa-f]{4}/;
function decodeUnicode(value) {
if (!unicodeEscapeTestRE.test(value)) return value;
return value.replace(unicodeEscapeRE, (_match, hex) => {
const codePoint = Number.parseInt(hex, 16);
return Number.isNaN(codePoint) ? _match : String.fromCharCode(codePoint);
});
}
function decodeUnicode2(input) {
if (!unicodeEscapeTestRE.test(input)) return input;
try {
return JSON.parse(`"${input}"`);
} catch (_error) {
return decodeUnicode(input);
}
}
//#endregion
//#region src/js/class-context.ts
const CLASS_LIKE_KEYWORDS = /* @__PURE__ */ new Set([
"class",
"classname",
"hoverclass",
"virtualhostclass",
"rootclass"
]);
const CLASS_HELPER_IDENTIFIERS = /* @__PURE__ */ new Set([
"cn",
"clsx",
"classnames",
"twmerge",
"cva",
"tv",
"cx",
"r"
]);
const DASH_CODE = 45;
const COLON_CODE = 58;
const UPPERCASE_A_CODE = 65;
const UPPERCASE_Z_CODE = 90;
const UNDERSCORE_CODE = 95;
const ASCII_MAX_CODE = 127;
const NORMALIZE_KEYWORD_REGEXP = /[-_:]/g;
function normalizeKeyword(name) {
const length = name.length;
let firstNormalizedIndex = -1;
for (let i = 0; i < length; i++) {
const code = name.charCodeAt(i);
if (code === DASH_CODE || code === UNDERSCORE_CODE || code === COLON_CODE || code >= UPPERCASE_A_CODE && code <= UPPERCASE_Z_CODE) {
firstNormalizedIndex = i;
break;
}
if (code > ASCII_MAX_CODE) return name.replace(NORMALIZE_KEYWORD_REGEXP, "").toLowerCase();
}
if (firstNormalizedIndex === -1) return name;
let normalized = name.slice(0, firstNormalizedIndex);
for (let i = firstNormalizedIndex; i < length; i++) {
const code = name.charCodeAt(i);
if (code === DASH_CODE || code === UNDERSCORE_CODE || code === COLON_CODE) continue;
if (code >= UPPERCASE_A_CODE && code <= UPPERCASE_Z_CODE) {
normalized += String.fromCharCode(code + 32);
continue;
}
if (code > ASCII_MAX_CODE) return name.replace(NORMALIZE_KEYWORD_REGEXP, "").toLowerCase();
normalized += name[i];
}
return normalized;
}
function readObjectKeyName(path) {
if (path.isIdentifier()) return path.node.name;
if (path.isStringLiteral()) return path.node.value;
if (path.isTemplateLiteral() && path.node.expressions.length === 0) return path.node.quasis[0]?.value.cooked ?? path.node.quasis[0]?.value.raw;
}
function isClassLikeObjectProperty(path, valuePath) {
if (!path.isObjectProperty()) return false;
if (path.get("value") !== valuePath) return false;
const keyName = readObjectKeyName(path.get("key"));
if (!keyName) return false;
return CLASS_LIKE_KEYWORDS.has(normalizeKeyword(keyName));
}
function isClassLikeJsxAttribute(path) {
if (!path.isJSXAttribute()) return false;
const namePath = path.get("name");
if (!namePath.isJSXIdentifier()) return false;
return CLASS_LIKE_KEYWORDS.has(normalizeKeyword(namePath.node.name));
}
function readCallHelperName(calleePath) {
if (calleePath.isIdentifier()) return calleePath.node.name;
if (calleePath.isMemberExpression()) {
const propertyPath = calleePath.get("property");
if (propertyPath.isIdentifier()) return propertyPath.node.name;
if (propertyPath.isStringLiteral()) return propertyPath.node.value;
}
}
function isClassLikeCallExpression(path, valuePath) {
if (!path.isCallExpression()) return false;
const helperName = readCallHelperName(path.get("callee"));
if (!helperName || !CLASS_HELPER_IDENTIFIERS.has(normalizeKeyword(helperName))) return false;
return path.get("arguments").some((argumentPath) => argumentPath.node === valuePath.node);
}
/**
* 判断字符串字面量是否处于 class 语义上下文。
* 仅用于受控兜底场景,避免将普通业务文本误判为 class。
*/
function isClassContextLiteralPath(path) {
let current = path;
while (current.parentPath) {
const parent = current.parentPath;
if (isClassLikeObjectProperty(parent, current)) return true;
if (isClassLikeJsxAttribute(parent)) return true;
if (isClassLikeCallExpression(parent, current)) return true;
current = parent;
}
return false;
}
//#endregion
//#region src/js/js-string-escape.ts
function jsStringEscape(value) {
return String(value).replaceAll(/[\n\r"'\\\u2028\u2029]/g, (character) => {
switch (character) {
case "\"":
case "'":
case "\\": return `\\${character}`;
case "\n": return "\\n";
case "\r": return "\\r";
case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029";
default: return character;
}
});
}
//#endregion
//#region src/js/replacement-cache.ts
const replacementCacheByEscapeMap = /* @__PURE__ */ new WeakMap();
const defaultReplacementCache = /* @__PURE__ */ new Map();
function getReplacementCacheStore(escapeMap) {
if (!escapeMap) return defaultReplacementCache;
let store = replacementCacheByEscapeMap.get(escapeMap);
if (!store) {
store = /* @__PURE__ */ new Map();
replacementCacheByEscapeMap.set(escapeMap, store);
}
return store;
}
function getReplacement(candidate, escapeMap, store = getReplacementCacheStore(escapeMap)) {
let cached = store.get(candidate);
if (cached === void 0) {
cached = replaceWxml(candidate, { escapeMap });
store.set(candidate, cached);
}
return cached;
}
//#endregion
//#region src/js/handlers.ts
const debug = createDebug("[js:handlers] ");
const WEAPP_TW_IGNORE_MARKER = "weapp-tw";
const IGNORE_MARKER = "ignore";
function hasIgnoreComment(node) {
const { leadingComments } = node;
if (!Array.isArray(leadingComments) || leadingComments.length === 0) return false;
for (const comment of leadingComments) {
const { value } = comment;
if (value.includes(WEAPP_TW_IGNORE_MARKER) && value.includes(IGNORE_MARKER)) return true;
}
return false;
}
function isConditionTestLiteral(path) {
let current = path;
while (current?.parentPath) {
const parent = current.parentPath;
if (parent.isConditionalExpression()) return parent.node.test === current.node;
if (parent.isBinaryExpression() || parent.isCallExpression() || parent.isLogicalExpression() || parent.isMemberExpression() || parent.isUnaryExpression()) {
current = parent;
continue;
}
return false;
}
return false;
}
function extractLiteralValue(path, { unescapeUnicode }) {
const { node } = path;
let offset = 0;
let original;
if (node.type === "StringLiteral") {
offset = 1;
original = node.value;
} else if (node.type === "TemplateElement") original = node.value.raw;
else original = "";
let literal = original;
if (unescapeUnicode && original.includes("\\u")) literal = decodeUnicode2(original);
return {
literal,
offset,
original
};
}
function createCandidatePlanResolver(options, classContext) {
const { escapeMap } = options;
const replacementCache = getReplacementCacheStore(escapeMap);
const transformOptions = classContext ? {
...options,
classContext
} : options;
let firstCandidate = "";
let firstPlan;
let cache;
const buildCandidatePlan = (candidate) => {
const result = resolveClassNameTransformWithResult(candidate, transformOptions);
if (result.decision === "skip") return { result };
let replacement;
if (result.decision === "escaped" && result.escapedValue) {
replacement = result.escapedValue;
replacementCache.set(candidate, replacement);
} else replacement = getReplacement(candidate, escapeMap, replacementCache);
return {
result,
replacement
};
};
return (candidate) => {
if (cache) {
const cached = cache.get(candidate);
if (cached) return cached;
} else if (firstPlan && candidate === firstCandidate) return firstPlan;
const plan = buildCandidatePlan(candidate);
if (!firstPlan) {
firstCandidate = candidate;
firstPlan = plan;
return plan;
}
if (!cache) {
cache = /* @__PURE__ */ new Map();
cache.set(firstCandidate, firstPlan);
}
cache.set(candidate, plan);
return plan;
};
}
function replaceHandleValue(path, options) {
const { needEscaped = false } = options;
const { classNameSet, alwaysEscape } = options;
const fallbackEnabled = shouldEnableArbitraryValueFallback(options);
if (!alwaysEscape && !fallbackEnabled && (!classNameSet || classNameSet.size === 0)) return;
if (hasIgnoreComment(path.node)) return;
if (isConditionTestLiteral(path)) return;
const { literal, original, offset } = extractLiteralValue(path, options);
const candidates = splitCandidateTokens(literal);
if (candidates.length === 0) return;
const debugEnabled = debug.enabled;
const classContext = options.wrapExpression || isClassContextLiteralPath(path);
let transformed = literal;
let mutated = false;
let matchedCandidateCount = 0;
let escapedDecisionCount = 0;
let fallbackDecisionCount = 0;
let escapedSamples;
let skippedSamples;
const resolveCandidatePlan = createCandidatePlanResolver(options, classContext);
for (const candidate of candidates) {
const plan = resolveCandidatePlan(candidate);
if (plan.result.decision === "skip") {
if (debugEnabled) {
if (!skippedSamples) skippedSamples = [];
if (skippedSamples.length < 6) skippedSamples.push(candidate);
}
continue;
}
if (debugEnabled) {
matchedCandidateCount += 1;
if (plan.result.decision === "escaped") {
escapedDecisionCount += 1;
if (!escapedSamples) escapedSamples = [];
if (escapedSamples.length < 6) escapedSamples.push(candidate);
}
if (plan.result.decision === "fallback") fallbackDecisionCount += 1;
}
const replaced = transformed.replace(candidate, plan.replacement);
if (replaced !== transformed) {
transformed = replaced;
mutated = true;
}
}
const node = path.node;
if (!mutated || typeof node.start !== "number" || typeof node.end !== "number") return;
if (debugEnabled) debug("runtimeSet size=%d fallbackTriggered=%s candidates=%d matched=%d escapedHits=%d skipped=%d file=%s escapedSamples=%s skippedSamples=%s", classNameSet?.size ?? 0, fallbackDecisionCount > 0, candidates.length, matchedCandidateCount, escapedDecisionCount, skippedSamples?.length ?? 0, options.filename ?? "unknown", escapedSamples?.join(",") || "-", skippedSamples?.join(",") || "-");
const start = node.start + offset;
const end = node.end - offset;
if (start >= end || transformed === original) return;
return {
start,
end,
value: needEscaped ? jsStringEscape(transformed) : transformed,
path
};
}
//#endregion
//#region src/js/sourceAnalysis.ts
function hasReplacementEntries(replacements) {
for (const key in replacements) if (Object.hasOwn(replacements, key)) return true;
return false;
}
function createModuleSpecifierReplacementToken(path, replacement) {
const node = path.node;
if (node.value === replacement) return;
if (typeof node.start !== "number" || typeof node.end !== "number") return;
const start = node.start + 1;
const end = node.end - 1;
if (start >= end) return;
return {
start,
end,
value: replacement,
path
};
}
function collectModuleSpecifierReplacementTokens(analysis, replacements) {
if (!hasReplacementEntries(replacements)) return [];
if (analysis.importDeclarations.size === 0 && analysis.exportDeclarations.size === 0 && analysis.requireCallPaths.length === 0 && analysis.walker.imports.size === 0) return [];
const tokens = [];
const applyReplacement = (path) => {
const replacement = replacements[path.node.value];
if (!replacement) return;
const token = createModuleSpecifierReplacementToken(path, replacement);
if (token) tokens.push(token);
};
for (const importPath of analysis.importDeclarations) {
const source = importPath.get("source");
if (source.isStringLiteral()) applyReplacement(source);
}
for (const exportPath of analysis.exportDeclarations) if (exportPath.isExportNamedDeclaration() || exportPath.isExportAllDeclaration()) {
const source = exportPath.get("source");
if (source && !Array.isArray(source) && source.isStringLiteral()) applyReplacement(source);
}
for (const literalPath of analysis.requireCallPaths) applyReplacement(literalPath);
for (const token of analysis.walker.imports) {
const replacement = replacements[token.source];
if (replacement) token.source = replacement;
}
return tokens;
}
//#endregion
//#region src/js/babel/process.ts
const optionVariantsCache = /* @__PURE__ */ new WeakMap();
function getNeedEscapedOptions(options, needEscaped) {
if (options.needEscaped === needEscaped) return options;
let cached = optionVariantsCache.get(options);
if (!cached) {
cached = {};
optionVariantsCache.set(options, cached);
}
if (needEscaped) {
if (!cached.stringLiteralOptions) cached.stringLiteralOptions = {
...options,
needEscaped: true
};
return cached.stringLiteralOptions;
}
if (!cached.templateLiteralOptions) cached.templateLiteralOptions = {
...options,
needEscaped: false
};
return cached.templateLiteralOptions;
}
function processUpdatedSource(rawSource, options, analysis) {
const { targetPaths, jsTokenUpdater, ignoredPaths } = analysis;
if (targetPaths.length === 0 && !options.moduleSpecifierReplacements && jsTokenUpdater.length === 0) return new MagicString(rawSource);
const replacementTokens = [];
for (const path of targetPaths) {
if (ignoredPaths.has(path)) continue;
const token = replaceHandleValue(path, path.isStringLiteral() ? getNeedEscapedOptions(options, true) : getNeedEscapedOptions(options, false));
if (token) replacementTokens.push(token);
}
if (options.moduleSpecifierReplacements) replacementTokens.push(...collectModuleSpecifierReplacementTokens(analysis, options.moduleSpecifierReplacements));
if (jsTokenUpdater.length + replacementTokens.length === 0) return new MagicString(rawSource);
const ms = new MagicString(rawSource);
jsTokenUpdater.push(...replacementTokens).filter((token) => !ignoredPaths.has(token.path)).updateMagicString(ms);
return ms;
}
//#endregion
//#region src/js/evalTransforms.ts
const evalHandlerOptionsCache = /* @__PURE__ */ new WeakMap();
const EVAL_SCOPE_ERROR_REGEXP = /pass a scope and parentPath|traversing a Program\/File/i;
function isEvalPath(path) {
if (path.isCallExpression()) return path.get("callee").isIdentifier({ name: "eval" });
return false;
}
function createEvalReplacementToken(path, updated) {
const node = path.node;
let offset = 0;
let original;
if (path.isStringLiteral()) {
offset = 1;
original = path.node.value;
} else if (path.isTemplateElement()) original = path.node.value.raw;
else original = "";
if (typeof node.start !== "number" || typeof node.end !== "number") return;
const start = node.start + offset;
const end = node.end - offset;
if (start >= end) return;
if (original === updated) return;
return {
start,
end,
value: path.isStringLiteral() ? jsStringEscape(updated) : updated,
path
};
}
function handleEvalStringLiteral(path, handlerOptions, updater, handler) {
const { code } = handler(path.node.value, handlerOptions);
if (!code) return;
const token = createEvalReplacementToken(path, code);
if (token) updater.addToken(token);
}
function handleEvalTemplateElement(path, handlerOptions, updater, handler) {
const { code } = handler(path.node.value.raw, handlerOptions);
if (!code) return;
const token = createEvalReplacementToken(path, code);
if (token) updater.addToken(token);
}
function getEvalStringHandlerOptions(options) {
if (options.needEscaped === false && options.generateMap === false) return options;
let cached = evalHandlerOptionsCache.get(options);
if (!cached) {
cached = {};
evalHandlerOptionsCache.set(options, cached);
}
if (!cached.stringLiteralOptions) cached.stringLiteralOptions = {
...options,
needEscaped: false,
generateMap: false
};
return cached.stringLiteralOptions;
}
function getEvalTemplateHandlerOptions(options) {
if (options.generateMap === false) return options;
let cached = evalHandlerOptionsCache.get(options);
if (!cached) {
cached = {};
evalHandlerOptionsCache.set(options, cached);
}
if (!cached.templateLiteralOptions) cached.templateLiteralOptions = {
...options,
generateMap: false
};
return cached.templateLiteralOptions;
}
function walkEvalExpression(path, options, updater, handler) {
const stringHandlerOptions = getEvalStringHandlerOptions(options);
const templateHandlerOptions = getEvalTemplateHandlerOptions(options);
const maybeTraverse = path?.traverse;
if (typeof maybeTraverse === "function") try {
return maybeTraverse.call(path, {
StringLiteral(innerPath) {
handleEvalStringLiteral(innerPath, stringHandlerOptions, updater, handler);
},
TemplateElement(innerPath) {
handleEvalTemplateElement(innerPath, templateHandlerOptions, updater, handler);
}
});
} catch (error) {
const msg = error?.message ?? "";
if (!EVAL_SCOPE_ERROR_REGEXP.test(msg)) throw error;
}
const getArgs = path?.get?.("arguments");
if (Array.isArray(getArgs)) {
for (const arg of getArgs) {
if (arg?.isStringLiteral?.()) {
handleEvalStringLiteral(arg, stringHandlerOptions, updater, handler);
continue;
}
if (arg?.isTemplateLiteral?.()) for (const quasi of arg.get("quasis")) handleEvalTemplateElement(quasi, templateHandlerOptions, updater, handler);
}
return;
}
const nodeArgs = path?.node?.arguments;
if (Array.isArray(nodeArgs)) {
for (const n of nodeArgs) if (n?.type === "StringLiteral") handleEvalStringLiteral({
node: n,
isStringLiteral: () => true
}, stringHandlerOptions, updater, handler);
else if (n?.type === "TemplateLiteral" && Array.isArray(n.quasis)) for (const q of n.quasis) handleEvalTemplateElement({
node: q,
isStringLiteral: () => false,
isTemplateElement: () => true
}, templateHandlerOptions, updater, handler);
}
}
//#endregion
//#region src/js/JsTokenUpdater.ts
/**
* Lightweight helper that batches updates to {@link MagicString}.
* It keeps the transformation logic out of the traversal code and makes
* it easier to reason about the order in which tokens are written back.
*/
var JsTokenUpdater = class {
constructor({ value } = {}) {
_defineProperty(this, "tokens", void 0);
this.tokens = value ? [...value] : [];
}
addToken(token) {
if (token) this.tokens.push(token);
}
push(...args) {
this.tokens.push(...args);
return this;
}
/**
* 待写入的 token 数量。
*/
get length() {
return this.tokens.length;
}
map(callbackfn) {
this.tokens = this.tokens.map(callbackfn);
return this;
}
filter(callbackfn) {
this.tokens = this.tokens.filter(callbackfn);
return this;
}
updateMagicString(ms) {
for (const { start, end, value } of this.tokens) ms.update(start, end, value);
return ms;
}
};
//#endregion
//#region src/js/module-graph/ignored-exports.ts
var IgnoredExportsTracker = class {
constructor(options) {
this.options = options;
_defineProperty(this, "ignoredExportNames", /* @__PURE__ */ new Map());
}
addIgnoredExport(filename, exportName) {
if (!exportName) return;
let pending = this.ignoredExportNames.get(filename);
if (!pending) {
pending = /* @__PURE__ */ new Set();
this.ignoredExportNames.set(filename, pending);
}
if (pending.has(exportName)) return;
pending.add(exportName);
const existing = this.options.modules.get(filename);
if (existing) this.applyIgnoredExportsToAnalysis(filename, existing.analysis);
}
registerIgnoredExportsFromTokens(resolved, tokens) {
for (const token of tokens) if (token.type === "ImportSpecifier") this.addIgnoredExport(resolved, token.imported);
else if (token.type === "ImportDefaultSpecifier") this.addIgnoredExport(resolved, "default");
}
applyIgnoredExportsToAnalysis(filename, analysis) {
const pending = this.ignoredExportNames.get(filename);
if (!pending || pending.size === 0) return;
const names = new Set(pending);
pending.clear();
const propagate = [];
for (const exportPath of analysis.exportDeclarations) {
if (names.size === 0) break;
if (exportPath.isExportDefaultDeclaration()) {
if (names.has("default")) {
analysis.walker.walkExportDefaultDeclaration(exportPath);
names.delete("default");
}
continue;
}
if (exportPath.isExportNamedDeclaration()) {
const source = exportPath.node.source?.value;
if (typeof source === "string") {
for (const spec of exportPath.get("specifiers")) {
if (!spec.isExportSpecifier()) continue;
const exported = spec.get("exported");
let exportedName;
if (exported.isIdentifier()) exportedName = exported.node.name;
else if (exported.isStringLiteral()) exportedName = exported.node.value;
if (!exportedName || !names.has(exportedName)) continue;
const local = spec.get("local");
if (local.isIdentifier()) {
propagate.push({
specifier: source,
exportName: local.node.name
});
names.delete(exportedName);
} else if (local.isStringLiteral()) {
propagate.push({
specifier: source,
exportName: local.node.value
});
names.delete(exportedName);
}
}
continue;
}
const declaration = exportPath.get("declaration");
if (declaration.isVariableDeclaration()) for (const decl of declaration.get("declarations")) {
const id = decl.get("id");
if (id.isIdentifier()) {
const exportName = id.node.name;
if (names.has(exportName)) {
analysis.walker.walkVariableDeclarator(decl);
names.delete(exportName);
}
}
}
for (const spec of exportPath.get("specifiers")) {
if (!spec.isExportSpecifier()) continue;
const exported = spec.get("exported");
let exportedName;
if (exported.isIdentifier()) exportedName = exported.node.name;
else if (exported.isStringLiteral()) exportedName = exported.node.value;
if (!exportedName || !names.has(exportedName)) continue;
const local = spec.get("local");
analysis.walker.walkNode(local);
names.delete(exportedName);
}
continue;
}
if (exportPath.isExportAllDeclaration()) {
const source = exportPath.node.source?.value;
if (typeof source === "string") {
for (const exportName of names) propagate.push({
specifier: source,
exportName
});
names.clear();
}
}
}
for (const { specifier, exportName } of propagate) {
let resolved;
try {
resolved = this.options.resolve(specifier, filename);
} catch {
resolved = void 0;
}
if (!resolved) {
pending.add(exportName);
continue;
}
if (this.options.filter && !this.options.filter(resolved, specifier, filename)) {
pending.add(exportName);
continue;
}
this.addIgnoredExport(resolved, exportName);
}
for (const name of names) pending.add(name);
}
};
//#endregion
//#region src/js/ModuleGraph.ts
var JsModuleGraph = class {
constructor(entry, graphOptions) {
_defineProperty(this, "modules", /* @__PURE__ */ new Map());
_defineProperty(this, "queue", []);
_defineProperty(this, "resolve", void 0);
_defineProperty(this, "load", void 0);
_defineProperty(this, "filter", void 0);
_defineProperty(this, "maxDepth", void 0);
_defineProperty(this, "baseOptions", void 0);
_defineProperty(this, "parserOptions", void 0);
_defineProperty(this, "rootFilename", void 0);
_defineProperty(this, "ignoredExports", void 0);
this.resolve = graphOptions.resolve;
this.load = graphOptions.load;
this.filter = graphOptions.filter;
this.maxDepth = graphOptions.maxDepth ?? Number.POSITIVE_INFINITY;
const { moduleGraph: _moduleGraph, filename: _ignoredFilename, ...rest } = entry.handlerOptions;
this.baseOptions = {
...rest,
filename: entry.filename
};
this.parserOptions = entry.handlerOptions.babelParserOptions;
this.rootFilename = entry.filename;
this.ignoredExports = new IgnoredExportsTracker({
resolve: this.resolve,
filter: this.filter,
modules: this.modules
});
this.modules.set(entry.filename, {
filename: entry.filename,
source: entry.source,
analysis: entry.analysis
});
this.queue.push({
filename: entry.filename,
depth: 0
});
}
build() {
this.collectDependencies();
let linked;
for (const [filename, state] of this.modules) {
if (filename === this.rootFilename) continue;
const childOptions = {
...this.baseOptions,
filename
};
const code = processUpdatedSource(state.source, childOptions, state.analysis).toString();
if (code !== state.source) {
if (!linked) linked = {};
linked[filename] = { code };
}
}
return linked;
}
collectDependencies() {
while (this.queue.length > 0) {
const { filename, depth } = this.queue.shift();
if (depth >= this.maxDepth) continue;
const state = this.modules.get(filename);
if (!state) continue;
const dependencySpecifiers = /* @__PURE__ */ new Map();
for (const token of state.analysis.walker.imports) {
if (!dependencySpecifiers.has(token.source)) dependencySpecifiers.set(token.source, []);
dependencySpecifiers.get(token.source).push(token);
}
for (const exportPath of state.analysis.exportDeclarations) if (exportPath.isExportAllDeclaration() || exportPath.isExportNamedDeclaration()) {
const source = exportPath.node.source?.value;
if (typeof source === "string" && !dependencySpecifiers.has(source)) dependencySpecifiers.set(source, []);
}
for (const [specifier, tokens] of dependencySpecifiers) {
let resolved;
try {
resolved = this.resolve(specifier, filename);
} catch {
continue;
}
if (!resolved) continue;
if (this.filter && !this.filter(resolved, specifier, filename)) continue;
if (tokens.length > 0) this.ignoredExports.registerIgnoredExportsFromTokens(resolved, tokens);
if (this.modules.has(resolved)) continue;
let source;
try {
source = this.load(resolved);
} catch {
continue;
}
if (typeof source !== "string") continue;
let analysis;
try {
analysis = analyzeSource(babelParse(source, {
...this.parserOptions,
sourceFilename: resolved
}), {
...this.baseOptions,
filename: resolved
});
this.ignoredExports.applyIgnoredExportsToAnalysis(resolved, analysis);
} catch {
continue;
}
this.modules.set(resolved, {
filename: resolved,
source,
analysis
});
this.queue.push({
filename: resolved,
depth: depth + 1
});
}
}
}
};
//#endregion
//#region src/js/node-path-walker/export-handlers.ts
function walkExportDeclaration(ctx, path) {
if (path.isExportDeclaration()) {
if (path.isExportNamedDeclaration()) walkExportNamedDeclaration(ctx, path);
else if (path.isExportDefaultDeclaration()) walkExportDefaultDeclaration(ctx, path);
else if (path.isExportAllDeclaration()) walkExportAllDeclaration(ctx, path);
}
}
function walkExportNamedDeclaration(ctx, path) {
const declaration = path.get("declaration");
if (declaration.isVariableDeclaration()) for (const decl of declaration.get("declarations")) ctx.walkNode(decl);
const specifiers = path.get("specifiers");
for (const spec of specifiers) if (spec.isExportSpecifier()) {
const local = spec.get("local");
if (local.isIdentifier()) ctx.walkNode(local);
}
}
function walkExportDefaultDeclaration(ctx, path) {
const decl = path.get("declaration");
if (decl.isIdentifier()) ctx.walkNode(decl);
else ctx.walkNode(decl);
}
function walkExportAllDeclaration(ctx, path) {
const source = path.get("source");
if (source.isStringLiteral()) ctx.addImportToken({
declaration: path,
source: source.node.value,
type: "ExportAllDeclaration"
});
}
//#endregion
//#region src/js/node-path-walker/import-tokens.ts
function maybeAddImportToken(imports, arg) {
if (!(arg.isImportSpecifier() && arg.node.importKind !== "type" || arg.isImportDefaultSpecifier())) return false;
const importDeclaration = arg.parentPath;
if (!importDeclaration.isImportDeclaration() || importDeclaration.node.importKind === "type") return false;
if (arg.isImportSpecifier()) {
const imported = arg.get("imported");
if (imported.isIdentifier()) imports.add({
declaration: importDeclaration,
specifier: arg,
imported: imported.node.name,
local: arg.node.local.name,
source: importDeclaration.node.source.value,
type: "ImportSpecifier"
});
return true;
}
imports.add({
declaration: importDeclaration,
specifier: arg,
local: arg.node.local.name,
source: importDeclaration.node.source.value,
type: "ImportDefaultSpecifier"
});
return true;
}
//#endregion
//#region src/js/NodePathWalker.ts
const EMPTY_IGNORE_CALL_EXPRESSION_IDENTIFIERS = [];
const EMPTY_IMPORT_TOKENS = /* @__PURE__ */ new Set();
function NOOP_STRING_PATH_CALLBACK() {}
const NEVER_MATCH_NAME = () => false;
/**
* 遍历我们关注的调用表达式所关联的绑定,收集后续需要转换的字符串节点。
*/
var NodePathWalker = class {
constructor({ ignoreCallExpressionIdentifiers, callback } = {}) {
_defineProperty(this, "ignoreCallExpressionIdentifiers", void 0);
_defineProperty(this, "callback", void 0);
_defineProperty(this, "isIgnoredCallIdentifier", void 0);
_defineProperty(this, "hasIgnoredCallIdentifiers", void 0);
_defineProperty(this, "importsStore", void 0);
_defineProperty(this, "visitedStore", void 0);
this.hasIgnoredCallIdentifiers = Boolean(ignoreCallExpressionIdentifiers && ignoreCallExpressionIdentifiers.length > 0);
this.ignoreCallExpressionIdentifiers = ignoreCallExpressionIdentifiers ?? EMPTY_IGNORE_CALL_EXPRESSION_IDENTIFIERS;
this.callback = callback ?? NOOP_STRING_PATH_CALLBACK;
this.isIgnoredCallIdentifier = this.hasIgnoredCallIdentifiers ? createNameMatcher(this.ignoreCallExpressionIdentifiers, { exact: true }) : NEVER_MATCH_NAME;
}
get imports() {
return this.importsStore ?? EMPTY_IMPORT_TOKENS;
}
getWritableImports() {
if (!this.importsStore) this.importsStore = /* @__PURE__ */ new Set();
return this.importsStore;
}
addImportToken(token) {
this.getWritableImports().add(token);
}
getVisited() {
if (!this.visitedStore) this.visitedStore = /* @__PURE__ */ new WeakSet();
return this.visitedStore;
}
walkVariableDeclarator(path) {
const init = path.get("init");
this.walkNode(init);
}
walkTemplateLiteral(path) {
for (const exp of path.get("expressions")) this.walkNode(exp);
for (const quasis of path.get("quasis")) this.callback(quasis);
}
walkStringLiteral(path) {
this.callback(path);
}
walkBinaryExpression(path) {
const left = path.get("left");
this.walkNode(left);
const right = path.get("right");
this.walkNode(right);
}
walkLogicalExpression(path) {
const left = path.get("left");
this.walkNode(left);
const right = path.get("right");
this.walkNode(right);
}
walkObjectExpression(path) {
const props = path.get("properties");
for (const prop of props) if (prop.isObjectProperty()) {
const key = prop.get("key");
this.walkNode(key);
const value = prop.get("value");
this.walkNode(value);
}
}
walkArrayExpression(path) {
const elements = path.get("elements");
for (const element of elements) this.walkNode(element);
}
walkNode(arg) {
const visited = this.getVisited();
if (visited.has(arg)) return;
visited.add(arg);
if (arg.isIdentifier()) {
const binding = arg?.scope?.getBinding?.(arg.node.name);
if (binding) this.walkNode(binding.path);
} else if (arg.isMemberExpression()) {
const objectPath = arg.get("object");
if (objectPath.isIdentifier()) {
const binding = arg?.scope?.getBinding?.(objectPath.node.name);
if (binding) {
if (binding.path.isVariableDeclarator()) this.walkVariableDeclarator(binding.path);
}
}
} else if (arg.isTemplateLiteral()) this.walkTemplateLiteral(arg);
else if (arg.isStringLiteral()) this.walkStringLiteral(arg);
else if (arg.isBinaryExpression()) this.walkBinaryExpression(arg);
else if (arg.isLogicalExpression()) this.walkLogicalExpression(arg);
else if (arg.isObjectExpression()) this.walkObjectExpression(arg);
else if (arg.isArrayExpression()) this.walkArrayExpression(arg);
else if (arg.isVariableDeclarator()) this.walkVariableDeclarator(arg);
else if (maybeAddImportToken(this.getWritableImports(), arg)) {}
}
/**
* Walk the arguments of a desired call expression so their bindings can be analysed.
*/
walkCallExpression(path) {
if (!this.hasIgnoredCallIdentifiers) return;
const calleePath = path.get("callee");
if (calleePath.isIdentifier() && this.isIgnoredCallIdentifier(calleePath.node.name)) for (const arg of path.get("arguments")) this.walkNode(arg);
}
walkExportDeclaration(path) {
walkExportDeclaration(this, path);
}
walkExportNamedDeclaration(path) {
walkExportNamedDeclaration(this, path);
}
walkExportDefaultDeclaration(path) {
walkExportDefaultDeclaration(this, path);
}
walkExportAllDeclaration(path) {
walkExportAllDeclaration(this, path);
}
};
//#endregion
//#region src/js/taggedTemplateIgnore.ts
function createTaggedTemplateIgnore({ matcher, names }) {
const bindingIgnoreCache = /* @__PURE__ */ new Map();
const taggedTemplateIgnoreCache = /* @__PURE__ */ new WeakMap();
const seenBindings = /* @__PURE__ */ new Set();
let singleCanonicalIgnoreName;
let canonicalIgnoreNames;
for (const item of names ?? []) {
if (typeof item !== "string") continue;
if (singleCanonicalIgnoreName === void 0) {
singleCanonicalIgnoreName = item;
continue;
}
if (item === singleCanonicalIgnoreName) continue;
if (!canonicalIgnoreNames) {
canonicalIgnoreNames = /* @__PURE__ */ new Set([singleCanonicalIgnoreName, item]);
continue;
}
canonicalIgnoreNames.add(item);
}
const hasCanonicalIgnoreNames = singleCanonicalIgnoreName !== void 0;
const matchesIgnoreName = (value) => {
if (hasCanonicalIgnoreNames) {
if (canonicalIgnoreNames) {
if (canonicalIgnoreNames.has(value)) return true;
} else if (value === singleCanonicalIgnoreName) return true;
}
return matcher(value);
};
const propertyMatches = (propertyPath) => {
if (!propertyPath) return false;
if (propertyPath.isIdentifier()) return matchesIgnoreName(propertyPath.node.name);
if (propertyPath.isStringLiteral()) return matchesIgnoreName(propertyPath.node.value);
return false;
};
const resolvesMemberExpressionToIgnore = (path, seen) => {
const propertyPath = path.get("property");
if (propertyMatches(propertyPath)) return true;
const objectPath = path.get("object");
if (objectPath.isIdentifier()) {
const binding = (objectPath?.scope)?.getBinding?.(objectPath.node.name);
if (binding) return resolvesToWeappTwIgnore(binding, seen);
}
return false;
};
const resolvesToWeappTwIgnore = (binding, seen) => {
const cached = bindingIgnoreCache.get(binding);
if (cached !== void 0) return cached;
if (seen.has(binding)) return false;
seen.add(binding);
let result = false;
const bindingPath = binding.path;
if (bindingPath.isImportSpecifier()) {
const imported = bindingPath.node.imported;
if (imported.type === "Identifier" && matchesIgnoreName(imported.name)) result = true;
else if (imported.type === "StringLiteral" && matchesIgnoreName(imported.value)) result = true;
} else if (bindingPath.isVariableDeclarator()) {
const init = bindingPath.get("init");
if (init && init.node) {
if (init.isIdentifier()) {
const target = binding?.scope?.getBinding?.(init.node.name);
if (target) result = resolvesToWeappTwIgnore(target, seen);
} else if (init.isMemberExpression()) result = resolvesMemberExpressionToIgnore(init, seen);
}
}
bindingIgnoreCache.set(binding, result);
seen.delete(binding);
return result;
};
const getEffectiveTagPath = (tagPath) => {
let current = tagPath;
while (true) {
if (current.isParenthesizedExpression?.() || current.node.type === "ParenthesizedExpression") {
current = current.get("expression");
continue;
}
if (current.isTSAsExpression() || current.isTSTypeAssertion()) {
current = current.get("expression");
continue;
}
if (current.isTSNonNullExpression()) {
current = current.get("expression");
continue;
}
if (current.isTypeCastExpression?.()) {
current = current.get("expression");
continue;
}
if (current.isSequenceExpression()) {
const last = current.get("expressions").at(-1);
if (last) {
current = last;
continue;
}
}
if (current.isCallExpression?.() || current.node.type === "CallExpression") {
current = current.get("callee");
continue;
}
break;
}
return current;
};
const evaluateTagPath = (tagPath, seen) => {
if (tagPath.isCallExpression?.() || tagPath.node.type === "CallExpression") {
const calleePath = tagPath.get("callee");
return evaluateTagPath(calleePath, seen);
}
if (tagPath.isIdentifier()) {
if (matchesIgnoreName(tagPath.node.name)) return true;
const binding = tagPath?.scope?.getBinding?.(tagPath.node.name);
if (binding) return resolvesToWeappTwIgnore(binding, seen);
return false;
}
if (tagPath.isMemberExpression()) return resolvesMemberExpressionToIgnore(tagPath, seen);
return false;
};
const computeIgnore = (tagPath) => {
const cached = taggedTemplateIgnoreCache.get(tagPath.node);
if (cached !== void 0) return cached;
const effectiveTagPath = getEffectiveTagPath(tagPath);
const effectiveCached = taggedTemplateIgnoreCache.get(effectiveTagPath.node);
if (effectiveCached !== void 0) {
taggedTemplateIgnoreCache.set(tagPath.node, effectiveCached);
return effectiveCached;
}
seenBindings.clear();
const result = evaluateTagPath(effectiveTagPath, seenBindings);
taggedTemplateIgnoreCache.set(effectiveTagPath.node, result);
taggedTemplateIgnoreCache.set(tagPath.node, result);
return result;
};
return {
shouldIgnore(tagPath) {
return computeIgnore(tagPath);
},
getEffectiveTagPath
};
}
//#endregion
//#region src/js/babel.ts
const EXPRESSION_WRAPPER_PREFIX = "(\n";
const EXPRESSION_WRAPPER_SUFFIX = "\n)";
const EMPTY_IGNORED_PATHS = /* @__PURE__ */ new WeakSet();
const EMPTY_IMPORT_DECLARATIONS = /* @__PURE__ */ new Set();
const EMPTY_EXPORT_DECLARATIONS = /* @__PURE__ */ new Set();
const EMPTY_REQUIRE_CALL_PATHS = [];
const ignoredTaggedTemplateMatcherCache = /* @__PURE__ */ new WeakMap();
let defaultEvalHandler;
function getIgnoredTaggedTemplateMatcher(options) {
const cached = ignoredTaggedTemplateMatcherCache.get(options);
if (cached) return cached;
const created = createNameMatcher(options.ignoreTaggedTemplateExpressionIdentifiers, { exact: true });
ignoredTaggedTemplateMatcherCache.set(options, created);
return created;
}
function getDefaultEvalHandler() {
if (!defaultEvalHandler) throw new Error("Default JS eval handler is not initialized.");
return defaultEvalHandler;
}
function analyzeSource(ast, options, handler, collectModuleMetadata = true) {
const jsTokenUpdater = new JsTokenUpdater();
const needScope = Boolean(options.ignoreCallExpressionIdentifiers && options.ignoreCallExpressionIdentifiers.length > 0);
const ignoredPaths = needScope ? /* @__PURE__ */ new WeakSet() : EMPTY_IGNORED_PATHS;
const walker = needScope ? new NodePathWalker({
...options.ignoreCallExpressionIdentifiers === void 0 ? {} : { ignoreCallExpressionIdentifiers: options.ignoreCallExpressionIdentifiers },
callback(path) {
ignoredPaths.add(path);
}
}) : new NodePathWalker();
let taggedTemplateIgnore;
const hasTaggedTemplateIgnoreIdentifiers = Boolean(options.ignoreTaggedTemplateExpressionIdentifiers && options.ignoreTaggedTemplateExpressionIdentifiers.length > 0);
function getTaggedTemplateIgnore() {
if (!taggedTemplateIgnore) taggedTemplateIgnore = createTaggedTemplateIgnore({
matcher: getIgnoredTaggedTemplateMatcher(options),
...options.ignoreTaggedTemplateExpressionIdentifiers === void 0 ? {} : { names: options.ignoreTaggedTemplateExpressionIdentifiers }
});
return taggedTemplateIgnore;
}
const targetPaths = [];
const importDeclarations = collectModuleMetadata ? /* @__PURE__ */ new Set() : EMPTY_IMPORT_DECLARATIONS;
const exportDeclarations = collectModuleMetadata ? /* @__PURE__ */ new Set() : EMPTY_EXPORT_DECLARATIONS;
const requireCallPaths = collectModuleMetadata ? [] : EMPTY_REQUIRE_CALL_PATHS;
const evalHandler = handler ?? getDefaultEvalHandler();
traverse(ast, {
StringLiteral: { enter(p) {
if (isEvalPath(p.parentPath)) return;
targetPaths.push(p);
} },
TemplateElement: { enter: hasTaggedTemplateIgnoreIdentifiers ? (p) => {
const pp = p.parentPath;
if (pp.isTemplateLiteral()) {
const ppp = pp.parentPath;
if (isEvalPath(ppp)) return;
if (ppp.isTaggedTemplateExpression()) {
const tagPath = ppp.get("tag");
if (getTaggedTemplateIgnore().shouldIgnore(tagPath)) return;
}
}
targetPaths.push(p);
} : (p