|
| 1 | +import type { OutputAsset } from 'rollup' |
| 2 | +import type { RememberedCssSource } from './types' |
| 3 | +import type { InternalUserDefinedOptions } from '@/types' |
| 4 | +import { hasBundlerGeneratedCssMarker, parseBundlerGeneratedCssMarkerBlocks } from '../../shared/generated-css-marker' |
| 5 | +import { hasTailwindApplyDirective } from '../../shared/generator-css/directives' |
| 6 | +import { normalizeOutputPathKey } from '../../shared/module-graph' |
| 7 | +import { isAppOriginCssFile, isMainAppCssFile, resolveViteCssPipelineOutputFile } from './css-output' |
| 8 | +import { hasTailwindGenerationSource } from './sfc-style-source' |
| 9 | +import { scoreMatchingStyleFileBase } from './style-matching' |
| 10 | + |
| 11 | +export function createRememberedCssRuntimeSignature(cssRuntimeSignature: string, cssRuntimeAffectingHash: string) { |
| 12 | + return `${cssRuntimeSignature}:${cssRuntimeAffectingHash}` |
| 13 | +} |
| 14 | + |
| 15 | +export function resolveRememberedCssSourceForTest( |
| 16 | + sources: Iterable<[string, RememberedCssSource]> | undefined, |
| 17 | + outputFile: string, |
| 18 | + file: string, |
| 19 | + originalSource: OutputAsset, |
| 20 | + outputRoot: string, |
| 21 | + sourceRoot: string | undefined, |
| 22 | +) { |
| 23 | + return findRememberedCssSource(sources, outputFile, file, originalSource, outputRoot, sourceRoot) |
| 24 | +} |
| 25 | + |
| 26 | +function findRememberedCssSource( |
| 27 | + sources: Iterable<[string, RememberedCssSource]> | undefined, |
| 28 | + outputFile: string, |
| 29 | + file: string, |
| 30 | + originalSource: OutputAsset, |
| 31 | + outputRoot: string, |
| 32 | + sourceRoot: string | undefined, |
| 33 | +) { |
| 34 | + const matched = findRememberedCssSources(sources, outputFile, file, originalSource, outputRoot, sourceRoot) |
| 35 | + return matched.length === 1 ? matched[0] : undefined |
| 36 | +} |
| 37 | + |
| 38 | +export function findRememberedCssSources( |
| 39 | + sources: Iterable<[string, RememberedCssSource]> | undefined, |
| 40 | + outputFile: string, |
| 41 | + file: string, |
| 42 | + originalSource: OutputAsset, |
| 43 | + outputRoot: string, |
| 44 | + sourceRoot: string | undefined, |
| 45 | +) { |
| 46 | + if (!sources) { |
| 47 | + return [] |
| 48 | + } |
| 49 | + const rememberedSources = [...sources].map(([, remembered]) => remembered) |
| 50 | + const source = typeof originalSource.source === 'string' |
| 51 | + ? originalSource.source |
| 52 | + : originalSource.source.toString() |
| 53 | + const markerFiles = new Set(parseBundlerGeneratedCssMarkerBlocks(source) |
| 54 | + .filter(block => block.bundler === 'vite' && typeof block.file === 'string' && block.file.length > 0) |
| 55 | + .map(block => normalizeOutputPathKey(block.file!))) |
| 56 | + if (markerFiles.size > 0) { |
| 57 | + const markerMatched = rememberedSources.filter(remembered => |
| 58 | + markerFiles.has(normalizeOutputPathKey(remembered.sourceFile.replace(/[?#].*$/, ''))), |
| 59 | + ) |
| 60 | + if (markerMatched.length > 0) { |
| 61 | + return markerMatched |
| 62 | + } |
| 63 | + } |
| 64 | + const originalFiles = [ |
| 65 | + file, |
| 66 | + originalSource.originalFileName, |
| 67 | + ...(originalSource.originalFileNames ?? []), |
| 68 | + ].filter((item): item is string => typeof item === 'string' && item.length > 0) |
| 69 | + |
| 70 | + const sourceMatched = rememberedSources.filter(remembered => |
| 71 | + originalFiles.some(originalFile => normalizeOutputPathKey(remembered.sourceFile) === normalizeOutputPathKey(originalFile)), |
| 72 | + ) |
| 73 | + if (sourceMatched.length > 0) { |
| 74 | + return sourceMatched |
| 75 | + } |
| 76 | + |
| 77 | + const outputMatched = rememberedSources.filter(remembered => |
| 78 | + normalizeOutputPathKey(remembered.outputFile) === normalizeOutputPathKey(outputFile), |
| 79 | + ) |
| 80 | + if (outputMatched.length > 0) { |
| 81 | + return outputMatched |
| 82 | + } |
| 83 | + |
| 84 | + const shouldUseRememberedApplyFallback = !hasBundlerGeneratedCssMarker(source) |
| 85 | + && !hasTailwindGenerationSource(source) |
| 86 | + if (shouldUseRememberedApplyFallback && !rememberedSources.some(remembered => hasTailwindApplyDirective(remembered.rawSource))) { |
| 87 | + return [] |
| 88 | + } |
| 89 | + |
| 90 | + const scoredMatches = rememberedSources |
| 91 | + .filter(remembered => !shouldUseRememberedApplyFallback || hasTailwindApplyDirective(remembered.rawSource)) |
| 92 | + .filter(remembered => !(isMainAppCssFile(outputFile) && isAppOriginCssFile(remembered.outputFile))) |
| 93 | + .map(remembered => ({ |
| 94 | + remembered, |
| 95 | + score: Math.max( |
| 96 | + scoreMatchingStyleFileBase(outputFile, remembered.sourceFile, outputRoot, sourceRoot), |
| 97 | + scoreMatchingStyleFileBase(outputFile, remembered.outputFile, outputRoot, sourceRoot), |
| 98 | + ), |
| 99 | + })) |
| 100 | + .filter(match => match.score > 0) |
| 101 | + .sort((a, b) => b.score - a.score) |
| 102 | + const bestScore = scoredMatches[0]?.score |
| 103 | + return bestScore |
| 104 | + ? scoredMatches.filter(match => match.score === bestScore).map(match => match.remembered) |
| 105 | + : [] |
| 106 | +} |
| 107 | + |
| 108 | +export function mergeRememberedCssSources( |
| 109 | + sources: RememberedCssSource[], |
| 110 | + outputFile: string, |
| 111 | +) { |
| 112 | + if (sources.length <= 1) { |
| 113 | + return sources[0] |
| 114 | + } |
| 115 | + const seen = new Set<string>() |
| 116 | + const rawSources: string[] = [] |
| 117 | + for (const source of sources) { |
| 118 | + const key = `${source.sourceFile}\0${source.rawSource}` |
| 119 | + if (seen.has(key)) { |
| 120 | + continue |
| 121 | + } |
| 122 | + seen.add(key) |
| 123 | + rawSources.push(source.rawSource) |
| 124 | + } |
| 125 | + return { |
| 126 | + outputFile, |
| 127 | + rawSource: rawSources.join('\n'), |
| 128 | + sourceFile: sources[0]?.sourceFile ?? outputFile, |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +export function collectRememberedCssReplayGroups( |
| 133 | + sources: Iterable<[string, RememberedCssSource]> | undefined, |
| 134 | + opts: Pick<InternalUserDefinedOptions, 'cssMatcher'>, |
| 135 | + rootDir: string, |
| 136 | + isWebGeneratorTarget: boolean, |
| 137 | + preserveCssExtension: boolean, |
| 138 | +) { |
| 139 | + const groups = new Map<string, Array<{ key: string, remembered: RememberedCssSource }>>() |
| 140 | + for (const [key, remembered] of sources ?? []) { |
| 141 | + const outputFile = resolveViteCssPipelineOutputFile( |
| 142 | + remembered.outputFile, |
| 143 | + opts, |
| 144 | + rootDir, |
| 145 | + isWebGeneratorTarget, |
| 146 | + preserveCssExtension, |
| 147 | + ) |
| 148 | + const outputKey = normalizeOutputPathKey(outputFile) |
| 149 | + const group = groups.get(outputKey) ?? [] |
| 150 | + group.push({ key, remembered }) |
| 151 | + groups.set(outputKey, group) |
| 152 | + } |
| 153 | + return groups |
| 154 | +} |
0 commit comments