Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor vite styles plugin #338

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
7 changes: 7 additions & 0 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export interface Options {
autoImport?: ImportPluginOptions,
styles?: true | 'none' | 'sass' | {
configFile: string,
/**
* Enable this flag when using Vite >= 5.4.3.
*
* @see https://github.com/vitejs/vite/pull/17909
* @default false
*/
useViteFileImport?: boolean,
},
}

Expand Down
73 changes: 28 additions & 45 deletions packages/vite-plugin/src/stylesPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
import path from 'upath'
import { resolveVuetifyBase, normalizePath, isObject } from '@vuetify/loader-shared'

import type { Plugin } from 'vite'
import type { Options } from '@vuetify/loader-shared'

function isSubdir (root: string, test: string) {
const relative = path.relative(root, test)
return relative && !relative.startsWith('..') && !path.isAbsolute(relative)
}

const PLUGIN_VIRTUAL_PREFIX = "virtual:"
const PLUGIN_VIRTUAL_NAME = "plugin-vuetify"
const VIRTUAL_MODULE_ID = `${PLUGIN_VIRTUAL_PREFIX}${PLUGIN_VIRTUAL_NAME}`
import { pathToFileURL } from 'node:url'

export function stylesPlugin (options: Options): Plugin {
const vuetifyBase = resolveVuetifyBase()

let configFile: string
let configFile: string | undefined
const tempFiles = new Map<string, string>()
const isNone = options.styles === 'none'
const sassVariables = isNone ? false : isObject(options.styles)
let useFileImport = false

return {
name: 'vuetify:styles',
enforce: 'pre',
configResolved (config) {
if (isObject(options.styles)) {
useFileImport = options.styles.useViteFileImport === true
if (path.isAbsolute(options.styles.configFile)) {
configFile = options.styles.configFile
configFile = path.resolve(options.styles.configFile)
} else {
configFile = path.join(config.root || process.cwd(), options.styles.configFile)
configFile = path.resolve(path.join(config.root || process.cwd(), options.styles.configFile))
}
configFile = normalizePath(configFile)
}
},
async resolveId (source, importer, { custom }) {
Expand All @@ -39,48 +35,35 @@ export function stylesPlugin (options: Options): Plugin {
isSubdir(vuetifyBase, path.isAbsolute(source) ? source : importer)
)
) {
if (options.styles === 'none') {
return `${PLUGIN_VIRTUAL_PREFIX}__void__`
} else if (options.styles === 'sass') {
if (options.styles === 'sass') {
const target = source.replace(/\.css$/, '.sass')
return this.resolve(target, importer, { skipSelf: true, custom })
} else if (isObject(options.styles)) {
const resolution = await this.resolve(source, importer, { skipSelf: true, custom })
}

if (!resolution) return null
const resolution = await this.resolve(source, importer, { skipSelf: true, custom })

const target = resolution.id.replace(/\.css$/, '.sass')
const file = path.relative(path.join(vuetifyBase, 'lib'), target)
const contents = `@use "${normalizePath(configFile)}"\n@use "${normalizePath(target)}"`
if (!resolution) return null
userquin marked this conversation as resolved.
Show resolved Hide resolved

tempFiles.set(file, contents)
const target = resolution.id.replace(/\.css$/, '.sass')
tempFiles.set(target, isNone
? '' :
useFileImport
? `@use "${pathToFileURL(configFile!).href}"\n@use "${pathToFileURL(target).href}"`
: `@use "${normalizePath(configFile!)}"\n@use "${normalizePath(target)}"`
)

return `${VIRTUAL_MODULE_ID}:${file}`
}
} else if (source.startsWith(`/${PLUGIN_VIRTUAL_NAME}:`)) {
return PLUGIN_VIRTUAL_PREFIX + source.slice(1)
} else if (source.startsWith(`/@id/__x00__${PLUGIN_VIRTUAL_NAME}:`)) {
return PLUGIN_VIRTUAL_PREFIX + source.slice(12)
} else if (source.startsWith(`/${VIRTUAL_MODULE_ID}:`)) {
return source.slice(1)
return target
}

return null
return undefined
},
load (id) {
// When Vite is configured with `optimizeDeps.exclude: ['vuetify']`, the
// received id contains a version hash (e.g. \0__void__?v=893fa859).
if (new RegExp(`^${PLUGIN_VIRTUAL_PREFIX}__void__(\\?.*)?$`).test(id)) {
return ''
}

if (id.startsWith(`${VIRTUAL_MODULE_ID}`)) {
const file = new RegExp(`^${VIRTUAL_MODULE_ID}:(.*?)(\\?.*)?$`).exec(id)![1]

return tempFiles.get(file)
}

return null
return isNone || sassVariables ? tempFiles.get(id) : undefined
},
}
}

function isSubdir (root: string, test: string) {
const relative = path.relative(root, test)
return relative && !relative.startsWith('..') && !path.isAbsolute(relative)
}