UNPKG

weapp-tailwindcss

Version:

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

398 lines (395 loc) 16.1 kB
import { applyTailwindcssCssImportRewrite, createLoaderAnchorFinders, ensureMpxTailwindcssAliases, getCacheKey, hasLoaderEntry, injectMpxCssRewritePreRules, isCssLikeModuleResource, isMpx, patchMpxLoaderResolve, setupMpxTailwindcssRedirect } from "./chunk-QJTJC5UT.mjs"; import { pushConcurrentTaskFactories, resolveDisabledOptions, resolveOutputSpecifier, resolvePackageDir, toAbsoluteOutputPath } from "./chunk-3SKEY32E.mjs"; import { processCachedTask } from "./chunk-RRHPTTCP.mjs"; import { setupPatchRecorder } from "./chunk-GIUNRP65.mjs"; import { collectRuntimeClassSet, createDebug, getCompilerContext, pluginName, refreshTailwindRuntimeState } from "./chunk-4Z6MHSEO.mjs"; import { getGroupedEntries } from "./chunk-RR5HCKVQ.mjs"; import { __dirname } from "./chunk-SM5V25IN.mjs"; // src/bundlers/webpack/BaseUnifiedPlugin/v5.ts import fs from "fs"; import path from "path"; import process from "process"; var debug = createDebug(); var weappTailwindcssPackageDir = resolvePackageDir("weapp-tailwindcss"); var UnifiedWebpackPluginV5 = class { constructor(options = {}) { this.options = getCompilerContext(options); this.appType = this.options.appType; } apply(compiler) { compiler.options = compiler.options || {}; const { mainCssChunkMatcher, disabled, onLoad, onUpdate, onEnd, onStart, styleHandler, templateHandler, jsHandler, runtimeLoaderPath, runtimeCssImportRewriteLoaderPath, cache, twPatcher: initialTwPatcher, refreshTailwindcssPatcher } = this.options; const disabledOptions = resolveDisabledOptions(disabled); const isTailwindcssV4 = (initialTwPatcher.majorVersion ?? 0) >= 4; const shouldRewriteCssImports = isTailwindcssV4 && this.options.rewriteCssImports !== false && !disabledOptions.rewriteCssImports; const isMpxApp = isMpx(this.appType); if (shouldRewriteCssImports) { applyTailwindcssCssImportRewrite(compiler, { pkgDir: weappTailwindcssPackageDir, enabled: true, appType: this.appType }); setupMpxTailwindcssRedirect(weappTailwindcssPackageDir, isMpxApp); } if (disabledOptions.plugin) { return; } const patchRecorderState = setupPatchRecorder(initialTwPatcher, this.options.tailwindcssBasedir, { source: "runtime", cwd: this.options.tailwindcssBasedir ?? process.cwd() }); const runtimeState = { twPatcher: initialTwPatcher, patchPromise: patchRecorderState.patchPromise, refreshTailwindcssPatcher, onPatchCompleted: patchRecorderState.onPatchCompleted }; const refreshRuntimeState = async (force) => { await refreshTailwindRuntimeState(runtimeState, force); }; const { Compilation, sources, NormalModule } = compiler.webpack; const { ConcatSource } = sources; async function getClassSetInLoader() { await refreshRuntimeState(true); await runtimeState.patchPromise; await collectRuntimeClassSet(runtimeState.twPatcher, { force: true, skipRefresh: true }); } const runtimeClassSetLoader = runtimeLoaderPath ?? path.resolve(__dirname, "./weapp-tw-runtime-classset-loader.js"); const runtimeCssImportRewriteLoader = shouldRewriteCssImports ? runtimeCssImportRewriteLoaderPath ?? path.resolve(__dirname, "./weapp-tw-css-import-rewrite-loader.js") : void 0; const runtimeClassSetLoaderExists = fs.existsSync(runtimeClassSetLoader); const runtimeCssImportRewriteLoaderExists = runtimeCssImportRewriteLoader ? fs.existsSync(runtimeCssImportRewriteLoader) : false; const runtimeLoaderRewriteOptions = shouldRewriteCssImports ? { pkgDir: weappTailwindcssPackageDir, appType: this.appType } : void 0; const classSetLoaderOptions = { getClassSet: getClassSetInLoader }; const { findRewriteAnchor, findClassSetAnchor } = createLoaderAnchorFinders(this.appType); const cssImportRewriteLoaderOptions = runtimeLoaderRewriteOptions ? { rewriteCssImports: runtimeLoaderRewriteOptions } : void 0; onLoad(); if (shouldRewriteCssImports && isMpxApp) { ensureMpxTailwindcssAliases(compiler, weappTailwindcssPackageDir); } if (runtimeCssImportRewriteLoader && shouldRewriteCssImports && cssImportRewriteLoaderOptions && isMpxApp) { injectMpxCssRewritePreRules(compiler, runtimeCssImportRewriteLoader, cssImportRewriteLoaderOptions); } const createRuntimeClassSetLoaderEntry = () => ({ loader: runtimeClassSetLoader, options: classSetLoaderOptions, ident: null, type: null }); const createCssImportRewriteLoaderEntry = () => { if (!runtimeCssImportRewriteLoader) { return null; } return { loader: runtimeCssImportRewriteLoader, options: cssImportRewriteLoaderOptions, ident: null, type: null }; }; compiler.hooks.compilation.tap(pluginName, (compilation) => { NormalModule.getCompilationHooks(compilation).loader.tap(pluginName, (_loaderContext, module) => { const hasRuntimeLoader = runtimeClassSetLoaderExists || runtimeCssImportRewriteLoaderExists; if (!hasRuntimeLoader) { return; } patchMpxLoaderResolve(_loaderContext, weappTailwindcssPackageDir, shouldRewriteCssImports && isMpxApp); const loaderEntries = module.loaders || []; let rewriteAnchorIdx = findRewriteAnchor(loaderEntries); const classSetAnchorIdx = findClassSetAnchor(loaderEntries); const isCssModule = isCssLikeModuleResource(module.resource, this.options.cssMatcher, this.appType); if (process.env.WEAPP_TW_LOADER_DEBUG && isCssModule) { debug("loader hook css module: %s loaders=%o anchors=%o", module.resource, loaderEntries.map((x) => x.loader), { rewriteAnchorIdx, classSetAnchorIdx }); } if (process.env.WEAPP_TW_LOADER_DEBUG && typeof module.resource === "string" && module.resource.includes("app.css")) { debug("app.css module loaders=%o anchors=%o", loaderEntries.map((x) => x.loader), { rewriteAnchorIdx, classSetAnchorIdx }); } else if (process.env.WEAPP_TW_LOADER_DEBUG && typeof module.resource === "string" && module.resource.endsWith(".css")) { debug("css module seen: %s loaders=%o anchors=%o", module.resource, loaderEntries.map((x) => x.loader), { rewriteAnchorIdx, classSetAnchorIdx }); } if (rewriteAnchorIdx === -1 && classSetAnchorIdx === -1 && !isCssModule) { return; } const anchorlessInsert = (entry, position) => { if (position === "after") { loaderEntries.push(entry); } else { loaderEntries.unshift(entry); } }; if (cssImportRewriteLoaderOptions && runtimeCssImportRewriteLoaderExists && runtimeCssImportRewriteLoader) { const existingIndex = loaderEntries.findIndex((entry) => entry.loader?.includes?.(runtimeCssImportRewriteLoader)); const rewriteLoaderEntry = existingIndex !== -1 ? loaderEntries.splice(existingIndex, 1)[0] : createCssImportRewriteLoaderEntry(); if (rewriteLoaderEntry) { const anchorIndex = findRewriteAnchor(loaderEntries); if (anchorIndex === -1) { anchorlessInsert(rewriteLoaderEntry, "after"); } else { loaderEntries.splice(anchorIndex + 1, 0, rewriteLoaderEntry); } rewriteAnchorIdx = findRewriteAnchor(loaderEntries); } } if (runtimeClassSetLoaderExists && !hasLoaderEntry(loaderEntries, runtimeClassSetLoader)) { const classSetLoaderEntry = createRuntimeClassSetLoaderEntry(); const anchorIndex = findClassSetAnchor(loaderEntries); if (anchorIndex === -1) { anchorlessInsert(classSetLoaderEntry, "before"); } else { const insertIndex = anchorIndex === -1 ? rewriteAnchorIdx : anchorIndex; loaderEntries.splice(insertIndex, 0, classSetLoaderEntry); } } }); compilation.hooks.processAssets.tapPromise( { name: pluginName, stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE }, async (assets) => { onStart(); debug("start"); await runtimeState.patchPromise; for (const chunk of compilation.chunks) { if (chunk.id && chunk.hash) { cache.calcHashValueChanged(chunk.id, chunk.hash); } } const entries = Object.entries(assets); const compilerOutputPath = compilation.compiler?.outputPath ?? compiler.outputPath; const outputDir = compilerOutputPath ? path.resolve(compilerOutputPath) : compilation.outputOptions?.path ?? process.cwd(); const jsAssets = /* @__PURE__ */ new Map(); for (const [file] of entries) { if (this.options.jsMatcher(file) || this.options.wxsMatcher(file)) { const absolute = toAbsoluteOutputPath(file, outputDir); jsAssets.set(absolute, file); } } const moduleGraphOptions = { resolve(specifier, importer) { return resolveOutputSpecifier(specifier, importer, outputDir, (candidate) => jsAssets.has(candidate)); }, load: (id) => { const assetName = jsAssets.get(id); if (!assetName) { return void 0; } const asset = compilation.getAsset(assetName); if (!asset) { return void 0; } const source = asset.source.source(); return typeof source === "string" ? source : source.toString(); }, filter(id) { return jsAssets.has(id); } }; const applyLinkedResults = (linked) => { if (!linked) { return; } for (const [id, { code }] of Object.entries(linked)) { const assetName = jsAssets.get(id); if (!assetName) { continue; } const asset = compilation.getAsset(assetName); if (!asset) { continue; } const previousSource = asset.source.source(); const previous = typeof previousSource === "string" ? previousSource : previousSource.toString(); if (previous === code) { continue; } const source = new ConcatSource(code); compilation.updateAsset(assetName, source); onUpdate(assetName, previous, code); debug("js linked handle: %s", assetName); } }; const groupedEntries = getGroupedEntries(entries, this.options); await refreshRuntimeState(true); await runtimeState.patchPromise; const runtimeSet = await collectRuntimeClassSet(runtimeState.twPatcher, { force: true, skipRefresh: true }); debug("get runtimeSet, class count: %d", runtimeSet.size); const tasks = []; if (Array.isArray(groupedEntries.html)) { for (const element of groupedEntries.html) { const [file, originalSource] = element; const rawSource = originalSource.source().toString(); const cacheKey = file; tasks.push( processCachedTask({ cache, cacheKey, rawSource, applyResult(source) { compilation.updateAsset(file, source); }, onCacheHit() { debug("html cache hit: %s", file); }, transform: async () => { const wxml = await templateHandler(rawSource, { runtimeSet }); const source = new ConcatSource(wxml); onUpdate(file, rawSource, wxml); debug("html handle: %s", file); return { result: source }; } }) ); } } const jsTaskFactories = []; if (Array.isArray(groupedEntries.js)) { for (const [file] of groupedEntries.js) { const cacheKey = getCacheKey(file); const asset = compilation.getAsset(file); if (!asset) { continue; } const absoluteFile = toAbsoluteOutputPath(file, outputDir); const initialSource = asset.source.source(); const initialRawSource = typeof initialSource === "string" ? initialSource : initialSource.toString(); jsTaskFactories.push(async () => { await processCachedTask({ cache, cacheKey, rawSource: initialRawSource, applyResult(source) { compilation.updateAsset(file, source); }, onCacheHit() { debug("js cache hit: %s", file); }, transform: async () => { const currentAsset = compilation.getAsset(file); const currentSourceValue = currentAsset?.source.source(); const currentSource = typeof currentSourceValue === "string" ? currentSourceValue : currentSourceValue?.toString() ?? ""; const { code, linked } = await jsHandler(currentSource, runtimeSet, { filename: absoluteFile, moduleGraph: moduleGraphOptions, babelParserOptions: { sourceFilename: absoluteFile } }); const source = new ConcatSource(code); onUpdate(file, currentSource, code); debug("js handle: %s", file); applyLinkedResults(linked); return { result: source }; } }); }); } } if (Array.isArray(groupedEntries.css)) { for (const element of groupedEntries.css) { const [file, originalSource] = element; const rawSource = originalSource.source().toString(); const cacheKey = file; tasks.push( processCachedTask({ cache, cacheKey, rawSource, applyResult(source) { compilation.updateAsset(file, source); }, onCacheHit() { debug("css cache hit: %s", file); }, transform: async () => { await runtimeState.patchPromise; const { css } = await styleHandler(rawSource, { isMainChunk: mainCssChunkMatcher(file, this.appType), postcssOptions: { options: { from: file } }, majorVersion: runtimeState.twPatcher.majorVersion }); const source = new ConcatSource(css); onUpdate(file, rawSource, css); debug("css handle: %s", file); return { result: source }; } }) ); } } pushConcurrentTaskFactories(tasks, jsTaskFactories); await Promise.all(tasks); debug("end"); onEnd(); } ); }); } }; export { weappTailwindcssPackageDir, UnifiedWebpackPluginV5 };