UNPKG

weapp-tailwindcss

Version:

把 tailwindcss 原子化样式思想,带给小程序开发者们! bring tailwindcss to miniprogram developers!

1,084 lines (1,064 loc) 29.8 kB
import { replaceWxml } from "./chunk-Q67IXIAH.mjs"; import { getDefaultOptions } from "./chunk-WKGE3KUO.mjs"; import { defuOverrideArray, isMap, regExpTest } from "./chunk-JXBLHLFR.mjs"; // src/cache/index.ts import { LRUCache } from "lru-cache"; // src/cache/md5.ts import crypto from "node:crypto"; function md5Hash(data) { return crypto.createHash("md5").update(data).digest("hex"); } // src/cache/index.ts function createCache(options) { const disabled = options === false; const hashMap = /* @__PURE__ */ new Map(); const instance = new LRUCache({ // 可能会添加和删除一些页面和组件, 先设定 1024 吧 max: 1024, ttl: 0, ttlAutopurge: false }); return { hashMap, instance, hasHashKey(key) { return hashMap.has(key); }, getHashValue(key) { return hashMap.get(key); }, setHashValue(key, value) { return hashMap.set(key, value); }, get(key) { return instance.get(key); }, set(key, value) { return instance.set(key, value); }, computeHash(message) { return md5Hash(message); }, calcHashValueChanged(key, hash) { const hit = this.getHashValue(key); if (hit) { this.setHashValue(key, { // new file should be changed changed: hash !== hit.hash, // new hash hash }); } else { this.setHashValue(key, { // new file should be changed changed: true, hash }); } return this; }, has(key) { return instance.has(key); }, async process(key, callback, fallback) { if (disabled) { const res = await fallback(); if (res) { this.set(res.key, res.source); } } else { const hit = this.getHashValue(key); if (hit && !hit.changed) { const returnFlag = await callback(); if (returnFlag !== false) { return; } } const res = await fallback(); if (res) { this.set(res.key, res.source); } } } }; } function initializeCache(cacheConfig) { if (typeof cacheConfig === "boolean" || cacheConfig === void 0) { return createCache(cacheConfig); } return cacheConfig; } // src/babel/index.ts import _babelTraverse from "@babel/traverse"; import { parse, parseExpression } from "@babel/parser"; function _interopDefaultCompat(e) { return e && typeof e === "object" && "default" in e ? e.default : e; } var traverse = _interopDefaultCompat(_babelTraverse); // src/js/babel.ts import { jsStringEscape as jsStringEscape2 } from "@ast-core/escape"; import { LRUCache as LRUCache2 } from "lru-cache"; import MagicString from "magic-string"; // src/js/handlers.ts import { jsStringEscape } from "@ast-core/escape"; import { escapeStringRegexp } from "@weapp-core/regex"; import { splitCode } from "@weapp-tailwindcss/shared/extractors"; // src/utils/decode.ts function decodeUnicode2(input) { try { return JSON.parse(`"${input}"`); } catch (_error) { return input; } } // src/js/handlers.ts function replaceHandleValue(path2, options) { const { classNameSet, escapeMap, mangleContext: ctx, needEscaped = false, jsPreserveClass, arbitraryValues, alwaysEscape, unescapeUnicode } = options; const allowDoubleQuotes = arbitraryValues?.allowDoubleQuotes; const offset = path2.isStringLiteral() ? 1 : 0; const str = path2.isStringLiteral() ? path2.node.value : path2.isTemplateElement() ? path2.node.value.raw : ""; let rawStr = str; let needUpdate = false; if (unescapeUnicode && rawStr.includes("\\u")) { rawStr = decodeUnicode2(rawStr); } const node = path2.node; const arr = splitCode(rawStr, allowDoubleQuotes); for (const v of arr) { if (alwaysEscape || classNameSet && classNameSet.has(v) && !jsPreserveClass?.(v)) { let ignoreFlag = false; if (Array.isArray(node.leadingComments)) { ignoreFlag = node.leadingComments.findIndex((x) => x.value.includes("weapp-tw") && x.value.includes("ignore")) > -1; } if (!ignoreFlag) { if (ctx) { rawStr = ctx.jsHandler(rawStr); } rawStr = rawStr.replace( new RegExp(escapeStringRegexp(v)), replaceWxml(v, { escapeMap }) ); needUpdate = true; } } } if (needUpdate && typeof node.start === "number" && typeof node.end === "number") { const start = node.start + offset; const end = node.end - offset; if (start < end && str !== rawStr) { const value = needEscaped ? jsStringEscape(rawStr) : rawStr; return { start, end, value, path: path2 }; } } } // src/js/JsTokenUpdater.ts var JsTokenUpdater = class { constructor({ value } = {}) { this.value = value ?? []; } addToken(token) { if (token) { this.value.push(token); } } push(...args) { this.value.push(...args); return this; } map(callbackfn) { this.value = this.value.map(callbackfn); return this; } filter(callbackfn) { this.value = this.value.filter(callbackfn); return this; } updateMagicString(ms) { for (const { start, end, value } of this.value) { ms.update(start, end, value); } return ms; } }; // src/js/NodePathWalker.ts var walkedBindingWeakMap = /* @__PURE__ */ new WeakMap(); var NodePathWalker = class { constructor({ ignoreCallExpressionIdentifiers, callback } = {}) { this.ignoreCallExpressionIdentifiers = ignoreCallExpressionIdentifiers ?? []; this.callback = callback ?? (() => { }); this.imports = /* @__PURE__ */ new Set(); } walkVariableDeclarator(path2) { const init = path2.get("init"); this.walkNode(init); } walkTemplateLiteral(path2) { for (const exp of path2.get("expressions")) { this.walkNode(exp); } for (const quasis of path2.get("quasis")) { this.callback(quasis); } } walkStringLiteral(path2) { this.callback(path2); } walkBinaryExpression(path2) { const left = path2.get("left"); this.walkNode(left); const right = path2.get("right"); this.walkNode(right); } walkLogicalExpression(path2) { const left = path2.get("left"); this.walkNode(left); const right = path2.get("right"); this.walkNode(right); } walkObjectExpression(path2) { const props = path2.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(path2) { const elements = path2.get("elements"); for (const element of elements) { this.walkNode(element); } } walkNode(arg) { if (walkedBindingWeakMap.get(arg)) { return; } walkedBindingWeakMap.set(arg, true); 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 (arg.isImportSpecifier() && arg.node.importKind !== "type" || arg.isImportDefaultSpecifier()) { const importDeclaration = arg.parentPath; if (importDeclaration.isImportDeclaration() && importDeclaration.node.importKind !== "type") { if (arg.isImportSpecifier()) { const imported = arg.get("imported"); if (imported.isIdentifier()) { this.imports.add( { declaration: importDeclaration, specifier: arg, imported: imported.node.name, source: importDeclaration.node.source.value, type: "ImportSpecifier" } ); } } else if (arg.isImportDefaultSpecifier()) { this.imports.add( { declaration: importDeclaration, specifier: arg, source: importDeclaration.node.source.value, type: "ImportDefaultSpecifier" } ); } } } } /** * @description main 方法 * @param path */ walkCallExpression(path2) { const calleePath = path2.get("callee"); if (calleePath.isIdentifier() && regExpTest(this.ignoreCallExpressionIdentifiers, calleePath.node.name, { exact: true })) { for (const arg of path2.get("arguments")) { this.walkNode(arg); } } } walkExportDeclaration(path2) { if (path2.isExportDeclaration()) { if (path2.isExportNamedDeclaration()) { this.walkExportNamedDeclaration(path2); } else if (path2.isExportDefaultDeclaration()) { this.walkExportDefaultDeclaration(path2); } else if (path2.isExportAllDeclaration()) { this.walkExportAllDeclaration(path2); } } } walkExportNamedDeclaration(path2) { const declaration = path2.get("declaration"); if (declaration.isVariableDeclaration()) { for (const decl of declaration.get("declarations")) { this.walkNode(decl); } } const specifiers = path2.get("specifiers"); for (const spec of specifiers) { if (spec.isExportSpecifier()) { const local = spec.get("local"); if (local.isIdentifier()) { this.walkNode(local); } } } } walkExportDefaultDeclaration(path2) { const decl = path2.get("declaration"); if (decl.isIdentifier()) { this.walkNode(decl); } } walkExportAllDeclaration(path2) { const source = path2.get("source"); if (source.isStringLiteral()) { this.imports.add( { declaration: path2, source: source.node.value, type: "ExportAllDeclaration" } ); } } }; // src/js/babel.ts var parseCache = new LRUCache2( { max: 512 } ); function genCacheKey(source, options) { return source + JSON.stringify(options, (_, val) => typeof val === "function" ? val.toString() : val); } function babelParse(code, { cache, ...options } = {}) { const cacheKey = genCacheKey(code, options); let result; if (cache) { result = parseCache.get(cacheKey); } if (!result) { result = parse(code, options); if (cache) { parseCache.set(cacheKey, result); } } return result; } function isEvalPath(p) { if (p.isCallExpression()) { const calleePath = p.get("callee"); return calleePath.isIdentifier( { name: "eval" } ); } return false; } var ignoreFlagMap = /* @__PURE__ */ new WeakMap(); function analyzeSource(ast, options) { const jsTokenUpdater = new JsTokenUpdater(); const walker = new NodePathWalker( { ignoreCallExpressionIdentifiers: options.ignoreCallExpressionIdentifiers, callback(path2) { if (path2.isStringLiteral() || path2.isTemplateElement()) { ignoreFlagMap.set(path2, true); } } } ); const targetPaths = []; const importDeclarations = /* @__PURE__ */ new Set(); const exportDeclarations = /* @__PURE__ */ new Set(); const traverseOptions = { StringLiteral: { enter(p) { if (isEvalPath(p.parentPath)) { return; } targetPaths.push(p); } }, TemplateElement: { enter(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 (tagPath.isIdentifier() && regExpTest(options.ignoreTaggedTemplateExpressionIdentifiers ?? [], tagPath.node.name, { exact: true })) { return; } } } targetPaths.push(p); } }, CallExpression: { enter(p) { if (isEvalPath(p)) { p.traverse({ StringLiteral: { enter(path2) { const { code } = jsHandler(path2.node.value, { ...options, needEscaped: false, generateMap: false }); if (code) { const node = path2.node; if (typeof node.start === "number" && typeof node.end === "number") { const start = node.start + 1; const end = node.end - 1; if (start < end && path2.node.value !== code) { jsTokenUpdater.addToken( { start, end, value: jsStringEscape2(code), path: path2 } ); } } } } }, TemplateElement: { enter(path2) { const { code } = jsHandler(path2.node.value.raw, { ...options, generateMap: false }); if (code) { const node = path2.node; if (typeof node.start === "number" && typeof node.end === "number") { const start = node.start; const end = node.end; if (start < end && path2.node.value.raw !== code) { jsTokenUpdater.addToken( { start, end, value: code, path: path2 } ); } } } } } }); return; } walker.walkCallExpression(p); } }, ImportDeclaration: { enter(p) { importDeclarations.add(p); } }, ExportDeclaration: { enter(p) { exportDeclarations.add(p); } } }; traverse(ast, traverseOptions); return { walker, jsTokenUpdater, ast, targetPaths, importDeclarations, exportDeclarations // jsTokens, }; } function processUpdatedSource(rawSource, options, analysis) { const ms = new MagicString(rawSource); const { targetPaths, jsTokenUpdater } = analysis; const tokens = targetPaths.filter( (x) => { return !ignoreFlagMap.get(x); } ).map( (p) => { if (p.isStringLiteral()) { return replaceHandleValue( p, { ...options, needEscaped: options.needEscaped ?? true } ); } else if (p.isTemplateElement()) { return replaceHandleValue( p, { ...options, needEscaped: false } ); } return void 0; } ).filter(Boolean); jsTokenUpdater.push( ...tokens ).filter( (x) => { return !ignoreFlagMap.get(x.path); } ).updateMagicString(ms); return ms; } function jsHandler(rawSource, options) { let ast; try { ast = babelParse(rawSource, options.babelParserOptions); } catch (error) { return { code: rawSource, error }; } const analysis = analyzeSource(ast, options); const ms = processUpdatedSource(rawSource, options, analysis); return { code: ms.toString(), get map() { return ms.generateMap(); } }; } // src/js/index.ts function createJsHandler(options) { const { mangleContext, arbitraryValues, escapeMap, jsPreserveClass, generateMap, babelParserOptions, ignoreCallExpressionIdentifiers, ignoreTaggedTemplateExpressionIdentifiers } = options; function handler(rawSource, classNameSet, options2) { const opts = defuOverrideArray(options2, { classNameSet, escapeMap, arbitraryValues, mangleContext, jsPreserveClass, generateMap, babelParserOptions, ignoreCallExpressionIdentifiers, ignoreTaggedTemplateExpressionIdentifiers }); return jsHandler(rawSource, opts); } return handler; } // src/tailwindcss/index.ts import { getPackageInfoSync } from "local-pkg"; // src/tailwindcss/patcher.ts import path from "node:path"; import process from "node:process"; import { defuOverrideArray as defuOverrideArray2 } from "@weapp-tailwindcss/shared"; import { TailwindcssPatcher } from "tailwindcss-patch"; function createTailwindcssPatcher(options) { const { basedir, cacheDir, supportCustomLengthUnitsPatch, tailwindcss, tailwindcssPatcherOptions } = options || {}; const cache = {}; if (cacheDir) { if (path.isAbsolute(cacheDir)) { cache.dir = cacheDir; } else if (basedir) { cache.dir = path.resolve(basedir, cacheDir); } else { cache.dir = path.resolve(process.cwd(), cacheDir); } } return new TailwindcssPatcher(defuOverrideArray2( tailwindcssPatcherOptions, { cache, patch: { basedir, applyPatches: { exportContext: true, extendLengthUnits: supportCustomLengthUnitsPatch }, tailwindcss, resolve: { paths: [ import.meta.url ] } } } )); } // src/wxml/utils.ts import * as t from "@babel/types"; import { Parser } from "htmlparser2"; import MagicString2 from "magic-string"; // src/wxml/Tokenizer.ts var Tokenizer = class { constructor() { this.reset(); } processChar(char, index) { switch (this.state) { case 0 /* START */: if (char === " ") { } else if (char === "{") { this.state = 2 /* OPEN_BRACE */; this.bufferStartIndex = index; this.buffer += char; this.expressionBuffer = char; this.expressionStartIndex = index; } else { this.state = 1 /* TEXT */; this.bufferStartIndex = index; this.buffer += char; } break; case 1 /* TEXT */: if (char === " ") { this.tokens.push({ start: this.bufferStartIndex, end: index, value: this.buffer, expressions: this.expressions }); this.buffer = ""; this.expressions = []; this.state = 0 /* START */; } else if (char === "{") { this.buffer += char; this.expressionBuffer = char; this.expressionStartIndex = index; this.state = 2 /* OPEN_BRACE */; } else { this.buffer += char; } break; case 2 /* OPEN_BRACE */: if (char === "}") { this.buffer += char; this.expressionBuffer += char; this.state = 3 /* POTENTIAL_CLOSE */; } else { this.buffer += char; this.expressionBuffer += char; } break; case 3 /* POTENTIAL_CLOSE */: if (char === "}") { this.buffer += char; this.expressionBuffer += char; this.expressions.push({ start: this.expressionStartIndex, end: index + 1, value: this.expressionBuffer }); this.expressionBuffer = ""; this.state = 4 /* BRACES_COMPLETE */; } else { this.buffer += char; this.expressionBuffer += char; this.state = 2 /* OPEN_BRACE */; } break; case 4 /* BRACES_COMPLETE */: if (char === " ") { this.tokens.push({ start: this.bufferStartIndex, end: index, value: this.buffer, expressions: this.expressions }); this.buffer = ""; this.expressions = []; this.state = 0 /* START */; } else if (char === "{") { this.expressionStartIndex = index; this.expressionBuffer = char; this.buffer += char; this.state = 2 /* OPEN_BRACE */; } else { this.buffer += char; this.state = 1 /* TEXT */; } break; default: throw new Error("Unexpected state"); } } run(input) { for (let i = 0; i < input.length; i++) { const char = input[i]; this.processChar(char, i); } if (this.buffer.length > 0) { this.tokens.push({ start: this.bufferStartIndex, end: input.length, value: this.buffer, expressions: this.expressions }); } return this.tokens; } reset() { this.state = 0 /* START */; this.buffer = ""; this.tokens = []; this.bufferStartIndex = 0; this.expressionBuffer = ""; this.expressionStartIndex = 0; this.expressions = []; } }; // src/wxml/utils.ts function generateCode(match, options = {}) { try { const { jsHandler: jsHandler2, runtimeSet } = options; if (jsHandler2 && runtimeSet) { const { code } = jsHandler2(match, runtimeSet); return code; } else { const ms = new MagicString2(match); const ast = parseExpression(match); const jsTokenUpdater = new JsTokenUpdater(); traverse(ast, { StringLiteral(path2) { if (t.isMemberExpression(path2.parent)) { return; } if (t.isBinaryExpression(path2.parent) && (t.isConditionalExpression(path2.parentPath?.parent) || t.isLogicalExpression(path2.parentPath?.parent))) { return; } jsTokenUpdater.addToken( replaceHandleValue( path2, { mangleContext: options.mangleContext, escapeMap: options.escapeMap, classNameSet: options.runtimeSet, needEscaped: true, alwaysEscape: true } ) ); }, noScope: true }); jsTokenUpdater.updateMagicString(ms); return ms.toString(); } } catch { return match; } } function handleEachClassFragment(ms, tokens, options = {}) { for (const token of tokens) { let p = token.start; if (token.expressions.length > 0) { for (const exp of token.expressions) { if (exp.start > token.start && p < exp.start) { ms.update(p, exp.start, replaceWxml(ms.slice(p, exp.start), { keepEOL: true, escapeMap: options.escapeMap, mangleContext: options.mangleContext, // 首的str才会被转译 // example: 2xl:xx 2x{{y}} ignoreHead: p > 0 })); } const code = `{{${generateCode(exp.value.slice(2, -2), options)}}}`; ms.update(exp.start, exp.end, code); p = exp.end; } if (token.end > p) { ms.update(p, token.end, replaceWxml(ms.slice(p, token.end), { keepEOL: false, escapeMap: options.escapeMap, mangleContext: options.mangleContext, ignoreHead: true })); } } else { ms.update(token.start, token.end, replaceWxml(token.value, { keepEOL: false, escapeMap: options.escapeMap, mangleContext: options.mangleContext, ignoreHead: false })); } } } function templateReplacer(original, options = {}) { const ms = new MagicString2(original); const tokenizer = new Tokenizer(); const tokens = tokenizer.run(ms.original); handleEachClassFragment(ms, tokens, options); return ms.toString(); } function regTest(reg, str) { reg.lastIndex = 0; return reg.test(str); } function isPropsMatch(props, attr) { if (Array.isArray(props)) { for (const prop of props) { const res = typeof prop === "string" ? prop.toLowerCase() === attr.toLowerCase() : regTest(prop, attr); if (res) { return res; } } return false; } else if (typeof props === "string") { return props === attr; } else { return regTest(props, attr); } } async function customTemplateHandler(rawSource, options) { const { customAttributesEntities = [], disabledDefaultTemplateHandler, inlineWxs, runtimeSet, jsHandler: jsHandler2 } = options ?? {}; const s = new MagicString2(rawSource); let tag = ""; const wxsArray = []; const parser = new Parser( { onopentagname(name) { tag = name; }, onattribute(name, value, quote) { if (value) { let update2 = function() { s.update( parser.startIndex + name.length + 2, // !important // htmlparser2 9.0.0: parser.endIndex // htmlparser2 9.1.0: parser.endIndex - 1 // https://github.com/sonofmagic/weapp-tailwindcss/issues/269 parser.endIndex - 1, templateReplacer(value, { ...options, quote }) ); }; var update = update2; if (!disabledDefaultTemplateHandler && ["class", "hover-class", "virtualhostclass"].includes(name.toLocaleLowerCase())) { update2(); } for (const [t2, props] of customAttributesEntities) { if (t2 === "*") { if (isPropsMatch(props, name)) { update2(); } } else if (typeof t2 === "string") { if (t2 === tag && isPropsMatch(props, name)) { update2(); } } else if (regTest(t2, tag) && isPropsMatch(props, name)) { update2(); } } } }, ontext(data) { if (inlineWxs && tag === "wxs") { wxsArray.push({ data, endIndex: parser.endIndex + 1, startIndex: parser.startIndex }); } }, onclosetag() { tag = ""; } }, { xmlMode: true } ); parser.write(s.original); parser.end(); for (const { data, endIndex, startIndex } of wxsArray) { const { code } = await jsHandler2(data, runtimeSet); s.update(startIndex, endIndex, code); } return s.toString(); } function createTemplateHandler(options = {}) { return (rawSource, opt = {}) => { return customTemplateHandler(rawSource, defuOverrideArray(opt, options)); }; } // src/context/index.ts import { useMangleStore } from "@weapp-tailwindcss/mangle"; import { createStyleHandler } from "@weapp-tailwindcss/postcss"; function getCompilerContext(opts) { const ctx = defuOverrideArray( opts, getDefaultOptions(), {} ); ctx.escapeMap = ctx.customReplaceDictionary; const { cssPreflight, customRuleCallback, cssPreflightRange, customAttributes, supportCustomLengthUnitsPatch, arbitraryValues, cssChildCombinatorReplaceValue, inlineWxs, injectAdditionalCssVarScope, jsPreserveClass, disabledDefaultTemplateHandler, cssSelectorReplacement, rem2rpx, cache, babelParserOptions, postcssOptions, cssRemoveProperty, cssRemoveHoverPseudoClass, escapeMap, mangle, tailwindcssBasedir, appType, ignoreCallExpressionIdentifiers, ignoreTaggedTemplateExpressionIdentifiers, cssPresetEnv, tailwindcss, tailwindcssPatcherOptions } = ctx; const customAttributesEntities = isMap(customAttributes) ? [...customAttributes.entries()] : Object.entries(customAttributes); const { initMangle, mangleContext, setMangleRuntimeSet } = useMangleStore(); initMangle(mangle); const styleHandler = createStyleHandler({ cssPreflight, customRuleCallback, cssPreflightRange, escapeMap, mangleContext, cssChildCombinatorReplaceValue, injectAdditionalCssVarScope, cssSelectorReplacement, rem2rpx, postcssOptions, cssRemoveProperty, cssRemoveHoverPseudoClass, cssPresetEnv }); const jsHandler2 = createJsHandler({ escapeMap, mangleContext, arbitraryValues, jsPreserveClass, generateMap: true, babelParserOptions, ignoreCallExpressionIdentifiers, ignoreTaggedTemplateExpressionIdentifiers }); const templateHandler = createTemplateHandler({ customAttributesEntities, escapeMap, mangleContext, inlineWxs, jsHandler: jsHandler2, disabledDefaultTemplateHandler }); ctx.styleHandler = styleHandler; ctx.jsHandler = jsHandler2; ctx.templateHandler = templateHandler; const twPatcher = createTailwindcssPatcher( { basedir: tailwindcssBasedir, cacheDir: appType === "mpx" ? "node_modules/tailwindcss-patch/.cache" : void 0, supportCustomLengthUnitsPatch: supportCustomLengthUnitsPatch ?? true, tailwindcss, tailwindcssPatcherOptions } ); ctx.setMangleRuntimeSet = setMangleRuntimeSet; ctx.cache = initializeCache(cache); ctx.twPatcher = twPatcher; return ctx; } export { getCompilerContext };