提交 6da01482 编写于 作者: fxy060608's avatar fxy060608

fix: preprocess css

上级 a5c3f1fb
......@@ -2686,9 +2686,9 @@ var serviceContext = (function (vue) {
}
function parseRedirectInfo() {
const weexPlus = weex.requireModule('plus');
const { path, query, extraData, userAction } = weexPlus.getRedirectInfo() || {};
const { path, query, extraData, userAction, fromAppid } = weexPlus.getRedirectInfo() || {};
const referrerInfo = {
appId: '',
appId: fromAppid,
extraData: {},
};
if (extraData) {
......
......@@ -28,6 +28,7 @@ import type Stylus from 'stylus'
import type Less from 'less'
import type { Alias } from 'types/alias'
import { transform, formatMessages } from 'esbuild'
import { preCss } from '../../../../preprocess'
// const debug = createDebugger('vite:css')
export interface CSSOptions {
......@@ -866,6 +867,12 @@ const sass: SassStylePreprocessor = (source, root, options, aliasResolver) =>
aliasResolver
)
export function preprocessCss(content: string) {
if (content.includes('#endif')) {
return preCss(content)
}
return content
}
/**
* relative url() inside \@imported sass and less files must be rebased to use
* root file as base.
......@@ -876,18 +883,22 @@ async function rebaseUrls(
alias: Alias[]
): Promise<Sass.ImporterReturnType> {
file = path.resolve(file) // ensure os-specific flashes
// 条件编译
const contents = preprocessCss(fs.readFileSync(file, 'utf-8'))
// in the same dir, no need to rebase
const fileDir = path.dirname(file)
const rootDir = path.dirname(rootFile)
if (fileDir === rootDir) {
return { file }
return { file, contents }
}
// no url()
const content = fs.readFileSync(file, 'utf-8')
if (!cssUrlRE.test(content)) {
return { file }
if (!cssUrlRE.test(contents)) {
return { file, contents }
}
const rebased = await rewriteCssUrls(content, (url) => {
const rebased = await rewriteCssUrls(contents, (url) => {
if (url.startsWith('/')) return url
// match alias, no need to rewrite
for (const { find } of alias) {
......
import path from 'path'
import { parse as parseUrl } from 'url'
import fs, { promises as fsp } from 'fs'
import mime from 'mime/lite'
import { Plugin } from '../plugin'
import { ResolvedConfig } from '../config'
import { cleanUrl } from '../utils'
import { FS_PREFIX } from '../constants'
import { OutputOptions, PluginContext, RenderedChunk } from 'rollup'
import MagicString from 'magic-string'
import { createHash } from 'crypto'
import { normalizePath } from '../utils'
export const assetUrlRE = /__VITE_ASSET__([a-z\d]{8})__(?:\$_(.*?)__)?/g
const rawRE = /(\?|&)raw(?:&|$)/
const urlRE = /(\?|&)url(?:&|$)/
export const chunkToEmittedAssetsMap = new WeakMap<RenderedChunk, Set<string>>()
const assetCache = new WeakMap<ResolvedConfig, Map<string, string>>()
const assetHashToFilenameMap = new WeakMap<
ResolvedConfig,
Map<string, string>
>()
// save hashes of the files that has been emitted in build watch
const emittedHashMap = new WeakMap<ResolvedConfig, Set<string>>()
/**
* Also supports loading plain strings with import text from './foo.txt?raw'
*/
export function assetPlugin(config: ResolvedConfig): Plugin {
// assetHashToFilenameMap initialization in buildStart causes getAssetFilename to return undefined
assetHashToFilenameMap.set(config, new Map())
return {
name: 'vite:asset',
buildStart() {
assetCache.set(config, new Map())
emittedHashMap.set(config, new Set())
},
resolveId(id) {
if (!config.assetsInclude(cleanUrl(id))) {
return
}
// imports to absolute urls pointing to files in /public
// will fail to resolve in the main resolver. handle them here.
const publicFile = checkPublicFile(id, config)
if (publicFile) {
return id
}
},
async load(id) {
if (id.startsWith('\0')) {
// Rollup convention, this id should be handled by the
// plugin that marked it with \0
return
}
// raw requests, read from disk
if (rawRE.test(id)) {
const file = checkPublicFile(id, config) || cleanUrl(id)
// raw query, read file and return as string
return `export default ${JSON.stringify(
await fsp.readFile(file, 'utf-8')
)}`
}
if (!config.assetsInclude(cleanUrl(id)) && !urlRE.test(id)) {
return
}
id = id.replace(urlRE, '$1').replace(/[\?&]$/, '')
const url = await fileToUrl(id, config, this)
return `export default ${JSON.stringify(url)}`
},
renderChunk(code, chunk) {
let match: RegExpExecArray | null
let s: MagicString | undefined
// Urls added with JS using e.g.
// imgElement.src = "my/file.png" are using quotes
// Urls added in CSS that is imported in JS end up like
// var inlined = ".inlined{color:green;background:url(__VITE_ASSET__5aa0ddc0__)}\n";
// In both cases, the wrapping should already be fine
while ((match = assetUrlRE.exec(code))) {
s = s || (s = new MagicString(code))
const [full, hash, postfix = ''] = match
// some internal plugins may still need to emit chunks (e.g. worker) so
// fallback to this.getFileName for that.
const file = getAssetFilename(hash, config) || this.getFileName(hash)
registerAssetToChunk(chunk, file)
const outputFilepath = config.base + file + postfix
s.overwrite(match.index, match.index + full.length, outputFilepath)
}
if (s) {
return {
code: s.toString(),
map: config.build.sourcemap ? s.generateMap({ hires: true }) : null,
}
} else {
return null
}
},
generateBundle(_, bundle) {
// do not emit assets for SSR build
if (config.command === 'build' && config.build.ssr) {
for (const file in bundle) {
if (
bundle[file].type === 'asset' &&
!file.includes('ssr-manifest.json')
) {
delete bundle[file]
}
}
}
},
}
}
export function registerAssetToChunk(chunk: RenderedChunk, file: string): void {
let emitted = chunkToEmittedAssetsMap.get(chunk)
if (!emitted) {
emitted = new Set()
chunkToEmittedAssetsMap.set(chunk, emitted)
}
emitted.add(cleanUrl(file))
}
export function checkPublicFile(
url: string,
{ publicDir }: ResolvedConfig
): string | undefined {
// note if the file is in /public, the resolver would have returned it
// as-is so it's not going to be a fully resolved path.
if (!publicDir || !url.startsWith('/')) {
return
}
const publicFile = path.join(publicDir, cleanUrl(url))
if (fs.existsSync(publicFile)) {
return publicFile
} else {
return
}
}
export function fileToUrl(
id: string,
config: ResolvedConfig,
ctx: PluginContext
): string | Promise<string> {
if (config.command === 'serve') {
return fileToDevUrl(id, config)
} else {
return fileToBuiltUrl(id, config, ctx)
}
}
function fileToDevUrl(id: string, config: ResolvedConfig) {
let rtn: string
if (checkPublicFile(id, config)) {
// in public dir, keep the url as-is
rtn = id
} else if (id.startsWith(config.root)) {
// in project root, infer short public path
rtn = '/' + path.posix.relative(config.root, id)
} else {
// outside of project root, use absolute fs path
// (this is special handled by the serve static middleware
rtn = path.posix.join(FS_PREFIX + id)
}
const origin = config.server?.origin ?? ''
return origin + config.base + rtn.replace(/^\//, '')
}
export function getAssetFilename(
hash: string,
config: ResolvedConfig
): string | undefined {
return assetHashToFilenameMap.get(config)?.get(hash)
}
/**
* converts the source filepath of the asset to the output filename based on the assetFileNames option. \
* this function imitates the behavior of rollup.js. \
* https://rollupjs.org/guide/en/#outputassetfilenames
*
* @example
* ```ts
* const content = Buffer.from('text');
* const fileName = assetFileNamesToFileName(
* 'assets/[name].[hash][extname]',
* '/path/to/file.txt',
* getAssetHash(content),
* content
* )
* // fileName: 'assets/file.982d9e3e.txt'
* ```
*
* @param assetFileNames filename pattern. e.g. `'assets/[name].[hash][extname]'`
* @param file filepath of the asset
* @param contentHash hash of the asset. used for `'[hash]'` placeholder
* @param content content of the asset. passed to `assetFileNames` if `assetFileNames` is a function
* @returns output filename
*/
export function assetFileNamesToFileName(
assetFileNames: Exclude<OutputOptions['assetFileNames'], undefined>,
file: string,
contentHash: string,
content: string | Buffer
): string {
const basename = path.basename(file)
// placeholders for `assetFileNames`
// `hash` is slightly different from the rollup's one
const extname = path.extname(basename)
const ext = extname.substr(1)
const name = basename.slice(0, -extname.length)
const hash = contentHash
if (typeof assetFileNames === 'function') {
assetFileNames = assetFileNames({
name: file,
source: content,
type: 'asset',
})
if (typeof assetFileNames !== 'string') {
throw new TypeError('assetFileNames must return a string')
}
} else if (typeof assetFileNames !== 'string') {
throw new TypeError('assetFileNames must be a string or a function')
}
const fileName = assetFileNames.replace(
/\[\w+\]/g,
(placeholder: string): string => {
switch (placeholder) {
case '[ext]':
return ext
case '[extname]':
return extname
case '[hash]':
return hash
case '[name]':
return name
}
throw new Error(
`invalid placeholder ${placeholder} in assetFileNames "${assetFileNames}"`
)
}
)
return fileName
}
/**
* Register an asset to be emitted as part of the bundle (if necessary)
* and returns the resolved public URL
*/
async function fileToBuiltUrl(
id: string,
config: ResolvedConfig,
pluginContext: PluginContext,
skipPublicCheck = false
): Promise<string> {
if (!skipPublicCheck && checkPublicFile(id, config)) {
return config.base + id.slice(1)
}
const cache = assetCache.get(config)!
const cached = cache.get(id)
if (cached) {
return cached
}
const file = cleanUrl(id)
const content = await fsp.readFile(file)
let url: string
if (
config.build.lib ||
(!file.endsWith('.svg') &&
content.length < Number(config.build.assetsInlineLimit))
) {
// base64 inlined as a string
url = `data:${mime.getType(file)};base64,${content.toString('base64')}`
} else {
// emit as asset
// rollup supports `import.meta.ROLLUP_FILE_URL_*`, but it generates code
// that uses runtime url sniffing and it can be verbose when targeting
// non-module format. It also fails to cascade the asset content change
// into the chunk's hash, so we have to do our own content hashing here.
// https://bundlers.tooling.report/hashing/asset-cascade/
// https://github.com/rollup/rollup/issues/3415
const map = assetHashToFilenameMap.get(config)!
const contentHash = getAssetHash(content)
const { search, hash } = parseUrl(id)
const postfix = (search || '') + (hash || '')
const output = config.build?.rollupOptions?.output
const assetFileNames =
(output && !Array.isArray(output) ? output.assetFileNames : undefined) ??
// defaults to '<assetsDir>/[name].[hash][extname]'
// slightly different from rollup's one ('assets/[name]-[hash][extname]')
path.posix.join(config.build.assetsDir, '[name].[hash][extname]')
const fileName = assetFileNamesToFileName(
assetFileNames,
file,
contentHash,
content
)
if (!map.has(contentHash)) {
map.set(contentHash, fileName)
}
const emittedSet = emittedHashMap.get(config)!
if (!emittedSet.has(contentHash)) {
const name = normalizePath(path.relative(config.root, file))
pluginContext.emitFile({
name,
fileName,
type: 'asset',
source: content,
})
emittedSet.add(contentHash)
}
url = `__VITE_ASSET__${contentHash}__${postfix ? `$_${postfix}__` : ``}`
}
cache.set(id, url)
return url
}
export function getAssetHash(content: Buffer): string {
return createHash('sha256').update(content).digest('hex').slice(0, 8)
}
export async function urlToBuiltUrl(
url: string,
importer: string,
config: ResolvedConfig,
pluginContext: PluginContext
): Promise<string> {
if (checkPublicFile(url, config)) {
return config.base + url.slice(1)
}
const file = url.startsWith('/')
? path.join(config.root, url)
: path.join(path.dirname(importer), url)
return fileToBuiltUrl(
file,
config,
pluginContext,
// skip public check since we just did it above
true
)
}
import type { Plugin, ResolvedConfig } from 'vite'
import { assetPlugin } from '../plugins/vitejs/plugins/asset'
import { cssPlugin, cssPostPlugin } from '../plugins/vitejs/plugins/css'
import { assetPlugin as h5AssetPlugin } from '../plugins/vitejs/plugins/h5Asset'
import {
cssPlugin as h5CssPlugin,
cssPostPlugin as h5CssPostPlugin,
} from '../plugins/vitejs/plugins/h5Css'
export type CreateUniViteFilterPlugin = (
opts: UniViteFilterPluginOptions
) => Plugin
......@@ -9,6 +14,18 @@ export interface UniViteFilterPluginOptions {
filter: (id: string) => boolean
}
export function injectH5AssetPlugin(config: ResolvedConfig) {
replacePlugins([h5AssetPlugin(config)], config)
}
export function injectH5CssPlugin(config: ResolvedConfig) {
replacePlugins([h5CssPlugin(config)], config)
}
export function injectH5CssPostPlugin(config: ResolvedConfig) {
replacePlugins([h5CssPostPlugin(config)], config)
}
export function injectAssetPlugin(config: ResolvedConfig) {
replacePlugins([assetPlugin(config)], config)
}
......
import type { ResolvedConfig } from 'vite'
import { UniVitePlugin } from '@dcloudio/uni-cli-shared'
import {
injectH5AssetPlugin,
injectH5CssPlugin,
injectH5CssPostPlugin,
UniVitePlugin,
} from '@dcloudio/uni-cli-shared'
import { createHandleHotUpdate } from './handleHotUpdate'
import { createTransformIndexHtml } from './transformIndexHtml'
import { createConfigureServer } from './configureServer'
......@@ -21,6 +26,10 @@ export function uniH5PLugin(): UniVitePlugin {
configOptions.resolvedConfig = config
// TODO 禁止 optimizeDeps
;(config as any).cacheDir = ''
injectH5AssetPlugin(config)
injectH5CssPlugin(config)
injectH5CssPostPlugin(config)
},
configureServer: createConfigureServer(),
handleHotUpdate: createHandleHotUpdate(),
......
......@@ -10765,10 +10765,11 @@ function usePageHeadSearchInput({
};
}
var _export_sfc = (sfc, props2) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props2) {
sfc[key] = val;
target[key] = val;
}
return sfc;
return target;
};
const _sfc_main = {
name: "PageRefresh",
......
......@@ -21564,10 +21564,11 @@ function usePageHeadSearchInput({
};
}
var _export_sfc = (sfc, props2) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props2) {
sfc[key] = val;
target[key] = val;
}
return sfc;
return target;
};
const _sfc_main = {
name: "PageRefresh",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册