weapp-tailwindcss
Version:
把 tailwindcss 原子化样式思想,带给小程序开发者们! bring tailwindcss to miniprogram developers!
395 lines (393 loc) • 15.3 kB
JavaScript
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
};