You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1 line
16 KiB
Plaintext

{"version":3,"sources":["../src/index.ts","../src/extractors/regex.ts","../src/extractors/default-extractor.ts"],"sourcesContent":["import { PurgeCSS } from 'purgecss';\nimport { defaultExtractor } from './extractors/default-extractor';\nimport { walk } from 'estree-walker';\nimport { join } from 'path';\nimport type { ResolvedConfig, Plugin } from 'vite';\nimport type { ComplexSafelist, StringRegExpArray, UserDefinedOptions } from 'purgecss';\n\ntype Extractor = (content: string) => string[];\n\ntype Options = Partial<UserDefinedOptions> & {\n\tsafelist?: ComplexSafelist;\n};\ntype PurgeOptions = Omit<Options, 'css'>;\n\nconst EXT_CSS = /\\.(css)$/;\nconst MAX_STRING_LITERAL_LENGTH = 50_000;\n\nexport function purgeCss(purgeOptions?: PurgeOptions): Plugin {\n\tlet viteConfig: ResolvedConfig;\n\tconst selectors = new Set<string>();\n\tconst standard: StringRegExpArray = [\n\t\t'*',\n\t\t'html',\n\t\t'body',\n\t\t/aria-current/,\n\t\t// fix for pseudo-class functions that begin with `:` getting purged (e.g. `:is`)\n\t\t// see: https://github.com/FullHuman/purgecss/issues/978\n\t\t/^\\:[-a-z]+$/,\n\t\t...(purgeOptions?.safelist?.standard ?? []),\n\t];\n\tconst extractor = (purgeOptions?.defaultExtractor as Extractor) ?? defaultExtractor();\n\tconst moduleIds = new Set<string>();\n\n\treturn {\n\t\tname: 'vite-plugin-tailwind-purgecss',\n\t\tapply: 'build',\n\t\tenforce: 'post',\n\n\t\tload(id) {\n\t\t\tif (EXT_CSS.test(id)) return;\n\t\t\tmoduleIds.add(id);\n\t\t},\n\n\t\tconfigResolved(config) {\n\t\t\tviteConfig = config;\n\t\t},\n\n\t\tasync generateBundle(options, bundle) {\n\t\t\ttype ChunkOrAsset = (typeof bundle)[string];\n\t\t\ttype Asset = Extract<ChunkOrAsset, { type: 'asset' }>;\n\t\t\tconst assets: Record<string, Asset> = {};\n\n\t\t\tfor (const id of moduleIds) {\n\t\t\t\tconst info = this.getModuleInfo(id);\n\t\t\t\tif (info?.isIncluded !== true || info.code === null) continue;\n\n\t\t\t\tconst ast = this.parse(info.code);\n\n\t\t\t\t// @ts-expect-error mismatched node types\n\t\t\t\twalk(ast, {\n\t\t\t\t\tenter(node, parent, key, index) {\n\t\t\t\t\t\tif (node.type === 'Literal' && typeof node.value === 'string') {\n\t\t\t\t\t\t\tnode.value.split(/\\s+/).forEach((word) => {\n\t\t\t\t\t\t\t\tif (word.length < MAX_STRING_LITERAL_LENGTH) {\n\t\t\t\t\t\t\t\t\textractor(word).forEach((selector) => selectors.add(selector));\n\t\t\t\t\t\t\t\t} else selectors.add(word);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (node.type === 'Identifier') {\n\t\t\t\t\t\t\tselectors.add(node.name);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (node.type === 'TemplateElement') {\n\t\t\t\t\t\t\tconst value = node.value.cooked ?? node.value.raw;\n\t\t\t\t\t\t\tvalue.split(/\\s+/).forEach((word) => {\n\t\t\t\t\t\t\t\tif (word.length < MAX_STRING_LITERAL_LENGTH) {\n\t\t\t\t\t\t\t\t\textractor(word).forEach((selector) => selectors.add(selector));\n\t\t\t\t\t\t\t\t} else selectors.add(word);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfor (const [fileName, chunkOrAsset] of Object.entries(bundle)) {\n\t\t\t\tif (chunkOrAsset.type === 'asset' && EXT_CSS.test(fileName)) {\n\t\t\t\t\tassets[fileName] = chunkOrAsset;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const selector of selectors) {\n\t\t\t\tstandard.push(selector);\n\t\t\t}\n\n\t\t\tfor (const [fileName, asset] of Object.entries(assets)) {\n\t\t\t\tconst purgeCSSResult = await new PurgeCSS().purge({\n\t\t\t\t\t...purgeOptions,\n\t\t\t\t\tcontent: [join(viteConfig.root, '**/*.html'), ...(purgeOptions?.content ?? [])],\n\t\t\t\t\tcss: [{ raw: (asset.source as string).trim(), name: fileName }],\n\t\t\t\t\trejected: true,\n\t\t\t\t\trejectedCss: true,\n\t\t\t\t\tsafelist: {\n\t\t\t\t\t\t...purgeOptions?.safelist,\n\t\t\t\t\t\tstandard,\n\t\t\t\t\t\tgreedy: [/svelte-/, /data-theme/, ...(purgeOptions?.safelist?.greedy ?? [])],\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (purgeCSSResult[0]) {\n\t\t\t\t\t// prevent the original from being written\n\t\t\t\t\tdelete bundle[asset.fileName];\n\n\t\t\t\t\t// emit the newly purged css file\n\t\t\t\t\tthis.emitFile({\n\t\t\t\t\t\t...