UNPKG

weapp-tailwindcss

Version:

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

395 lines (393 loc) 15.3 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 "./chunk-SZOXLSNK.mjs"; import "./chunk-TFOTUR4L.mjs"; import { getGroupedEntries } from "./chunk-RR5HCKVQ.mjs"; import { __dirname } from "./chunk-SM5V25IN.mjs"; // src/bundlers/webpack/BaseUnifiedPlugin/v4.ts import fs from "fs"; import path from "path"; import process from "process"; import { ConcatSource } from "webpack-sources"; var debug = createDebug(); var weappTailwindcssPackageDir = resolvePackageDir("weapp-tailwindcss"); var UnifiedWebpackPluginV4 = 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); }; 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) => { compilation.hooks.normalModuleLoader.tap(pluginName, (_loaderContext, module) => { const hasRuntimeLoader = runtimeClassSetLoaderExists || runtimeCssImportRewriteLoaderExists; if (!hasRuntimeLoader) { return; } if (shouldRewriteCssImports && isMpx(this.appType) && typeof _loaderContext.resolve === "function") { patchMpxLoaderResolve(_loaderContext, weappTailwindcssPackageDir, true); } 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 (runtimeLoaderRewriteOptions && runtimeCssImportRewriteLoaderExists && cssImportRewriteLoaderOptions && runtimeCssImportRewriteLoader) { const existingIndex = loaderEntries.findIndex( (entry) => entry.loader?.includes?.(runtimeCssImportRewriteLoader) ); const rewriteEntry = existingIndex !== -1 ? loaderEntries.splice(existingIndex, 1)[0] : createCssImportRewriteLoaderEntry(); if (rewriteEntry) { const anchorIndex = findRewriteAnchor(loaderEntries); if (anchorIndex === -1) { anchorlessInsert(rewriteEntry, "after"); } else { loaderEntries.splice(anchorIndex + 1, 0, rewriteEntry); } rewriteAnchorIdx = findRewriteAnchor(loaderEntries); } } if (runtimeClassSetLoaderExists && !hasLoaderEntry(loaderEntries, runtimeClassSetLoader)) { const anchorIndex = findClassSetAnchor(loaderEntries); if (anchorIndex === -1) { anchorlessInsert(createRuntimeClassSetLoaderEntry(), "before"); } else { const insertIndex = anchorIndex === -1 ? rewriteAnchorIdx : anchorIndex; loaderEntries.splice(insertIndex, 0, createRuntimeClassSetLoaderEntry()); } } }); }); compiler.hooks.emit.tapPromise(pluginName, async (compilation) => { await runtimeState.patchPromise; onStart(); debug("start"); for (const chunk of compilation.chunks) { if (chunk.id && chunk.hash) { cache.calcHashValueChanged(chunk.id, chunk.hash); } } const assets = compilation.assets; const entries = Object.entries(assets); const outputDir = compiler.options?.output?.path ? path.resolve(compiler.options.output.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 assetSource = compilation.assets[assetName]; if (!assetSource) { return void 0; } const source = assetSource.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 assetSource = compilation.assets[assetName]; if (!assetSource) { continue; } const previousSource = assetSource.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 assetSource = compilation.assets[file]; if (!assetSource) { continue; } const initialValue = assetSource.source(); const initialRawSource = typeof initialValue === "string" ? initialValue : initialValue.toString(); const absoluteFile = toAbsoluteOutputPath(file, outputDir); 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.assets[file]; const currentValue = currentAsset?.source(); const currentSource = typeof currentValue === "string" ? currentValue : currentValue?.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 { UnifiedWebpackPluginV4, weappTailwindcssPackageDir };