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!: update auto import and styles Vite plugins #271

Merged
merged 10 commits into from
Aug 29, 2024
10 changes: 6 additions & 4 deletions modern-sass-compiler/assets/main.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
@use 'vuetify' with (
$utilities: false,
$color-pack: false,
);
.gradient {
&.primary {
background: linear-gradient(to top right, #2563eb, #4b8efb);
color: white;
}
}
5 changes: 2 additions & 3 deletions modern-sass-compiler/assets/settings.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* DON'T USE @use here */
@forward 'vuetify/settings' with (
@use 'vuetify/settings' with (
/*$utilities: false,*/
$button-height: 40px,
$button-border-radius: 8px
);
8 changes: 4 additions & 4 deletions modern-sass-compiler/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export default defineNuxtConfig({
compatibilityDate: '2024-08-15',
ssr: true,
sourcemap: true,
imports: {
autoImport: true,
Expand All @@ -16,7 +17,8 @@ export default defineNuxtConfig({
},
viewportSize: true,
},
// styles: { configFile: 'assets/settings.scss' },
// styles: 'sass',
styles: { configFile: 'assets/settings.scss' },
},
},
vite: {
Expand Down Expand Up @@ -51,12 +53,10 @@ export default defineNuxtConfig({
],
},
},
/*
css: ['~/assets/main.scss'],
*/
features: {
devLogs: false,
// inlineStyles: false,
inlineStyles: false,
userquin marked this conversation as resolved.
Show resolved Hide resolved
userquin marked this conversation as resolved.
Show resolved Hide resolved
},
experimental: {
payloadExtraction: false,
Expand Down
1 change: 0 additions & 1 deletion modern-sass-compiler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"@nuxt/devtools": "latest",
"@unocss/nuxt": "^0.62.1",
"nuxt": "^3.12.4",
"sass": "^1.77.8",
"sass-embedded": "^1.77.8",
"typescript": "^5.5.4",
"vue-tsc": "^2.0.29",
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vuetify-nuxt-module",
"type": "module",
"version": "0.17.1",
"packageManager": "pnpm@9.7.1",
"packageManager": "pnpm@9.9.0",
"description": "Zero-Config Nuxt Module for Vuetify",
"author": "userquin <[email protected]>",
"license": "MIT",
Expand Down Expand Up @@ -73,7 +73,8 @@
"perfect-debounce": "^1.0.0",
"ufo": "^1.5.4",
"unconfig": "^0.5.5",
"vite-plugin-vuetify": "^2.0.3",
"upath": "^2.0.1",
"vite-plugin-vuetify": "^2.0.4",
"vuetify": "^3.7.0"
},
"devDependencies": {
Expand Down
1,257 changes: 392 additions & 865 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { getPackageInfo } from 'local-pkg'
import type { HookResult } from '@nuxt/schema'
import type { VuetifyOptions, createVuetify } from 'vuetify'
import { version as VITE_VERSION } from 'vite'
import { version } from '../package.json'
import type {
InlineModuleOptions,
Expand Down Expand Up @@ -62,6 +63,10 @@ export default defineNuxtModule<VuetifyModuleOptions>({
&& versions.length > 1
&& (versions[0] > 3 || (versions[0] === 3 && versions[1] >= 5))

const viteVersion = VITE_VERSION.split('.')
.map((v: string) => v.includes('-') ? v.split('-')[0] : v)
.map(v => Number.parseInt(v)) as VuetifyNuxtContext['viteVersion']

const ctx: VuetifyNuxtContext = {
logger,
resolver: createResolver(import.meta.url),
Expand All @@ -79,6 +84,7 @@ export default defineNuxtModule<VuetifyModuleOptions>({
labComponentsPromise: undefined!,
vuetify3_4,
vuetify3_5,
viteVersion,
}

await load(options, nuxt, ctx)
Expand Down
1 change: 1 addition & 0 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface VuetifyNuxtContext {
labComponentsPromise: Promise<VuetifyComponentsImportMap>
vuetify3_4?: boolean
vuetify3_5?: boolean
viteVersion: [major: number, minor: number, patch: number]
}

export async function loadVuetifyConfiguration<U extends ExternalVuetifyOptions>(
Expand Down
37 changes: 25 additions & 12 deletions src/utils/configure-vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,30 @@ export function configureVite(configKey: string, nuxt: Nuxt, ctx: VuetifyNuxtCon
}

if (!ctx.moduleOptions.disableModernSassCompiler) {
viteInlineConfig.css ??= {}
viteInlineConfig.css.preprocessorOptions ??= {}
viteInlineConfig.css.preprocessorOptions.sass ??= {}
const sassEmbedded = isPackageExists('sass-embedded')
if (sassEmbedded) {
viteInlineConfig.css.preprocessorOptions.sass.api = 'modern-compiler'
}
else {
viteInlineConfig.css.preprocessorOptions.sass.api = 'modern'
if (!('preprocessorMaxWorkers' in viteInlineConfig.css))
viteInlineConfig.css.preprocessorMaxWorkers = true
// vite version >= 5.4.0
const [major, minor, patch] = ctx.viteVersion
const enableModernSassCompiler = major > 5 || (major === 5 && minor >= 4)
if (enableModernSassCompiler) {
const sassEmbedded = isPackageExists('sass-embedded')
if (sassEmbedded) {
// vite version >= 5.4.2
// check https://github.com/vitejs/vite/pull/17754 and https://github.com/vitejs/vite/pull/17728
const omit = major > 5 || (major === 5 && minor > 4) || (major === 5 && minor === 4 && patch >= 2)
if (!omit) {
viteInlineConfig.css ??= {}
viteInlineConfig.css.preprocessorOptions ??= {}
viteInlineConfig.css.preprocessorOptions.sass ??= {}
viteInlineConfig.css.preprocessorOptions.sass.api = 'modern-compiler'
}
}
else {
viteInlineConfig.css ??= {}
viteInlineConfig.css.preprocessorOptions ??= {}
viteInlineConfig.css.preprocessorOptions.sass ??= {}
viteInlineConfig.css.preprocessorOptions.sass.api = 'modern'
if (!('preprocessorMaxWorkers' in viteInlineConfig.css))
viteInlineConfig.css.preprocessorMaxWorkers = true
}
}
}

Expand All @@ -69,7 +82,7 @@ export function configureVite(configKey: string, nuxt: Nuxt, ctx: VuetifyNuxtCon
}

viteInlineConfig.plugins.push(vuetifyImportPlugin(vuetifyImportOptions))
viteInlineConfig.plugins.push(vuetifyStylesPlugin({ styles: ctx.moduleOptions.styles }, ctx.logger))
viteInlineConfig.plugins.push(vuetifyStylesPlugin({ styles: ctx.moduleOptions.styles }, ctx.viteVersion, ctx.logger))
viteInlineConfig.plugins.push(vuetifyConfigurationPlugin(ctx))
viteInlineConfig.plugins.push(vuetifyIconsPlugin(ctx))
viteInlineConfig.plugins.push(vuetifyDateConfigurationPlugin(ctx))
Expand Down
21 changes: 16 additions & 5 deletions src/vite/vuetify-import-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { extname } from 'node:path'
import { pathToFileURL } from 'node:url'
import type { Plugin } from 'vite'
import { createFilter } from 'vite'
import type { Options } from '@vuetify/loader-shared'
import { generateImports } from '@vuetify/loader-shared'
import { parseQuery, parseURL } from 'ufo'
Expand All @@ -24,19 +24,30 @@ function parseId(id: string) {
}

export function vuetifyImportPlugin(options: Options) {
let filter: (id: unknown) => boolean
return <Plugin>{
name: 'vuetify:import:nuxt',
configResolved(config) {
if (config.plugins.findIndex(plugin => plugin.name === 'vuetify:import') > -1)
throw new Error('Remove vite-plugin-vuetify from your Nuxt config file, this module registers a modified version.')

const vueIdx = config.plugins.findIndex(plugin => plugin.name === 'vite:vue')
const vueOptions = vueIdx > -1 ? config.plugins[vueIdx].api?.options : {}
filter = createFilter(vueOptions.include, vueOptions.exclude)
},
async transform(code, id) {
const { query, path } = parseId(id)

if (
((!query || !('vue' in query)) && extname(path) === '.vue' && !/^import { render as _sfc_render } from ".*"$/m.test(code))
|| (query && 'vue' in query && (query.type === 'template' || (query.type === 'script' && query.setup === 'true')))
) {
const isVueVirtual = query && 'vue' in query
const isVueFile = !isVueVirtual
&& filter(path)
&& !/^import { render as _sfc_render } from ".*"$/m.test(code)
const isVueTemplate = isVueVirtual && (
query.type === 'template'
|| (query.type === 'script' && query.setup === 'true')
)

if (isVueFile || isVueTemplate) {
const { code: imports, source } = generateImports(code, options)
return {
code: source + imports,
Expand Down
112 changes: 65 additions & 47 deletions src/vite/vuetify-styles-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import process from 'node:process'
import { pathToFileURL } from 'node:url'
import type { Plugin } from 'vite'
import { normalizePath, resolveVuetifyBase } from '@vuetify/loader-shared'
import { isAbsolute, join, relative as relativePath } from 'pathe'
import { isObject, normalizePath, resolveVuetifyBase } from '@vuetify/loader-shared'
import { isAbsolute, relative as relativePath } from 'pathe'
import type { Options } from '@vuetify/loader-shared'

function isSubdir(root: string, test: string) {
const relative = relativePath(root, test)
return relative && !relative.startsWith('..') && !isAbsolute(relative)
}
import path from 'upath'
import type { VuetifyNuxtContext } from '../utils/config'

export function vuetifyStylesPlugin(
options: Options,
[major, minor, patch]: VuetifyNuxtContext['viteVersion'],
_logger: ReturnType<typeof import('@nuxt/kit')['useLogger']>,
) {
let configFile: string | undefined
// let cacheDir: string | undefined
const vuetifyBase = resolveVuetifyBase()

let configFile: string
const tempFiles = new Map<string, string>()
const noneFiles = new Set<string>()
let isNone = false
let sassVariables = false
let fileImport = false
const PREFIX = 'vuetify-styles/'
const SSR_PREFIX = `/@${PREFIX}`

return <Plugin>{
name: 'vuetify:styles:nuxt',
Expand All @@ -25,64 +29,78 @@ export function vuetifyStylesPlugin(
if (config.plugins.findIndex(plugin => plugin.name === 'vuetify:styles') > -1)
throw new Error('Remove vite-plugin-vuetify from your Nuxt config file, this module registers a modified version.')

if (typeof options.styles === 'object') {
if (isAbsolute(options.styles.configFile))
configFile = options.styles.configFile
if (isObject(options.styles)) {
sassVariables = true
// use file import when vite version > 5.4.2
// check https://github.com/vitejs/vite/pull/17909
fileImport = major > 5 || (major === 5 && minor > 4) || (major === 5 && minor === 4 && patch > 2)
if (path.isAbsolute(options.styles.configFile))
configFile = path.resolve(options.styles.configFile)
else
configFile = join(config.root || process.cwd(), options.styles.configFile)
configFile = path.resolve(path.join(config.root || process.cwd(), options.styles.configFile))

configFile = fileImport
? pathToFileURL(configFile).href
: normalizePath(configFile)
}
else {
isNone = options.styles === 'none'
}
},
async resolveId(source, importer, { custom, ssr }) {
if (source.startsWith(PREFIX) || source.startsWith(SSR_PREFIX))
return source

if (
source === 'vuetify/styles' || (
importer
&& source.endsWith('.css')
&& isSubdir(vuetifyBase, isAbsolute(source) ? source : importer)
&& isSubdir(vuetifyBase, path.isAbsolute(source) ? source : importer)
)
) {
if (options.styles === 'none') {
return '/@plugin-vuetify/lib/__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 (typeof options.styles === 'object') {
const resolution = await this.resolve(source, importer, { skipSelf: true, custom })

if (!resolution)
return null

const target = resolution.id.replace(/\.css$/, '.sass')
const file = relativePath(join(vuetifyBase, 'lib'), target)
const contents = `@use "${normalizePath(configFile)}"\n@use "${normalizePath(target)}"`

tempFiles.set(file, contents)
const resolution = await this.resolve(source, importer, { skipSelf: true, custom })
if (!resolution)
return undefined

return ssr
? `/@plugin-vuetify/lib/${file}`
: `/@fs/plugin-vuetify/lib/${file}`
const target = resolution.id.replace(/\.css$/, '.sass')
if (isNone) {
noneFiles.add(target)
return target
}

return `${ssr ? SSR_PREFIX : PREFIX}${path.relative(vuetifyBase, target)}`
}

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 (/^\/@plugin-vuetify\/lib\/__void__(\?.*)?$/.test(id))
return ''

if (id.startsWith('/@plugin-vuetify/lib/')) {
const file = /^\/@plugin-vuetify\/lib\/(.*?)(\?.*)?$/.exec(id)![1]
return tempFiles.get(file)
}
if (sassVariables) {
const target = id.startsWith(PREFIX)
? path.resolve(vuetifyBase, id.slice(PREFIX.length))
: id.startsWith(SSR_PREFIX)
? path.resolve(vuetifyBase, id.slice(SSR_PREFIX.length))
: undefined

if (id.startsWith('/@fs/plugin-vuetify/lib/')) {
const file = /^\/@fs\/plugin-vuetify\/lib\/(.*?)(\?.*)?$/.exec(id)![1]
return tempFiles.get(file)
if (target) {
return {
code: `@use "${configFile}"\n@use "${fileImport ? pathToFileURL(target).href : normalizePath(target)}"`,
map: {
mappings: '',
},
}
}
}

if (id.includes('plugin-vuetify/lib'))
return ''
return isNone && noneFiles.has(id) ? '' : undefined
},
}
}

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