diff --git a/package-lock.json b/package-lock.json index 52e8dbd0a5..d4899359c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "@commitlint/config-conventional": "17.7.0", "@fullhuman/postcss-purgecss": "5.0.0", "@nuxt/module-builder": "0.5.1", - "@nuxt/webpack-builder": "3.7.2", + "@nuxt/webpack-builder": "3.7.1", "@nuxtjs/eslint-config": "12.0.0", "@nuxtjs/eslint-module": "4.1.0", "@nuxtjs/stylelint-module": "5.1.0", @@ -52,7 +52,7 @@ "husky": "8.0.3", "jsdom": "22.1.0", "lint-staged": "14.0.1", - "nuxt": "3.7.2", + "nuxt": "^3.7.2", "pinst": "3.0.0", "playwright": "1.37.1", "postcss-functions": "4.0.2", @@ -2747,18 +2747,18 @@ } }, "node_modules/@nuxt/kit": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.7.2.tgz", - "integrity": "sha512-z3oW52Ar/nQvyUR2/s5AemcQJDv/EeRto6OUYa7/J9oMOmSpXJ39eldiggn4GV+2jSFCPAYozS/yIKtXYiPo/A==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.7.1.tgz", + "integrity": "sha512-8k4q+92qLz5z7RdSOKrEJIjM63xXBg0z/WhTtZgXv1R5ULZ77usdTMjQYhQ+Kgd1NMkpIXeKaAO6903xrSt53Q==", "dependencies": { - "@nuxt/schema": "3.7.2", + "@nuxt/schema": "3.7.1", "c12": "^1.4.2", "consola": "^3.2.3", "defu": "^6.1.2", "globby": "^13.2.2", "hash-sum": "^2.0.0", "ignore": "^5.2.4", - "jiti": "^1.20.0", + "jiti": "^1.19.3", "knitwork": "^1.0.0", "mlly": "^1.4.2", "pathe": "^1.1.1", @@ -2826,9 +2826,9 @@ } }, "node_modules/@nuxt/schema": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-3.7.2.tgz", - "integrity": "sha512-hajUPjpXD3TYfv1PL1UO2wCp8YOasMAnQI9keAYbO3gvMdvyf1SyIUWr2nQOmJDIC0bW4+mLNHFGhGf1dgUVoQ==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-3.7.1.tgz", + "integrity": "sha512-+0W/oos7Ktm3eTwQ/78PrcAObR0+yQzHIUzbQ7HgUnEEntRGVxp4hnfng5dmhvVjJqQvpuGZHa3yIS/g41vE6A==", "dependencies": { "@nuxt/ui-templates": "^1.3.1", "defu": "^6.1.2", @@ -2981,6 +2981,56 @@ "vue": "^3.3.4" } }, + "node_modules/@nuxt/vite-builder/node_modules/@nuxt/kit": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.7.2.tgz", + "integrity": "sha512-z3oW52Ar/nQvyUR2/s5AemcQJDv/EeRto6OUYa7/J9oMOmSpXJ39eldiggn4GV+2jSFCPAYozS/yIKtXYiPo/A==", + "dev": true, + "dependencies": { + "@nuxt/schema": "3.7.2", + "c12": "^1.4.2", + "consola": "^3.2.3", + "defu": "^6.1.2", + "globby": "^13.2.2", + "hash-sum": "^2.0.0", + "ignore": "^5.2.4", + "jiti": "^1.20.0", + "knitwork": "^1.0.0", + "mlly": "^1.4.2", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "scule": "^1.0.0", + "semver": "^7.5.4", + "ufo": "^1.3.0", + "unctx": "^2.3.1", + "unimport": "^3.3.0", + "untyped": "^1.4.0" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@nuxt/schema": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-3.7.2.tgz", + "integrity": "sha512-hajUPjpXD3TYfv1PL1UO2wCp8YOasMAnQI9keAYbO3gvMdvyf1SyIUWr2nQOmJDIC0bW4+mLNHFGhGf1dgUVoQ==", + "dev": true, + "dependencies": { + "@nuxt/ui-templates": "^1.3.1", + "defu": "^6.1.2", + "hookable": "^5.5.3", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "postcss-import-resolver": "^2.0.0", + "std-env": "^3.4.3", + "ufo": "^1.3.0", + "unimport": "^3.3.0", + "untyped": "^1.4.0" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/@nuxt/vite-builder/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3201,13 +3251,13 @@ "dev": true }, "node_modules/@nuxt/webpack-builder": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@nuxt/webpack-builder/-/webpack-builder-3.7.2.tgz", - "integrity": "sha512-QcDjN3u6BCF3EbjOpGi4Dz/1GWmo+fk2FA13XlLM8IcridXUV2v5FWkLenlx+nqhRPqLtsqKiTv5chlR3v8FGQ==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@nuxt/webpack-builder/-/webpack-builder-3.7.1.tgz", + "integrity": "sha512-VFnKAJY64JXo4y3NtOxx5PFLxmERxZBtGxl9Te+9hz8hfzpUVAl4yhx6kBq3B2otY6PJcHbdOkosZVCUkYOfKw==", "dev": true, "dependencies": { "@nuxt/friendly-errors-webpack-plugin": "^2.5.2", - "@nuxt/kit": "3.7.2", + "@nuxt/kit": "3.7.1", "autoprefixer": "^10.4.15", "css-loader": "^6.8.1", "css-minimizer-webpack-plugin": "^5.0.1", @@ -13348,12 +13398,95 @@ } } }, + "node_modules/nuxt/node_modules/@nuxt/kit": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.7.2.tgz", + "integrity": "sha512-z3oW52Ar/nQvyUR2/s5AemcQJDv/EeRto6OUYa7/J9oMOmSpXJ39eldiggn4GV+2jSFCPAYozS/yIKtXYiPo/A==", + "dev": true, + "dependencies": { + "@nuxt/schema": "3.7.2", + "c12": "^1.4.2", + "consola": "^3.2.3", + "defu": "^6.1.2", + "globby": "^13.2.2", + "hash-sum": "^2.0.0", + "ignore": "^5.2.4", + "jiti": "^1.20.0", + "knitwork": "^1.0.0", + "mlly": "^1.4.2", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "scule": "^1.0.0", + "semver": "^7.5.4", + "ufo": "^1.3.0", + "unctx": "^2.3.1", + "unimport": "^3.3.0", + "untyped": "^1.4.0" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/nuxt/node_modules/@nuxt/schema": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-3.7.2.tgz", + "integrity": "sha512-hajUPjpXD3TYfv1PL1UO2wCp8YOasMAnQI9keAYbO3gvMdvyf1SyIUWr2nQOmJDIC0bW4+mLNHFGhGf1dgUVoQ==", + "dev": true, + "dependencies": { + "@nuxt/ui-templates": "^1.3.1", + "defu": "^6.1.2", + "hookable": "^5.5.3", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "postcss-import-resolver": "^2.0.0", + "std-env": "^3.4.3", + "ufo": "^1.3.0", + "unimport": "^3.3.0", + "untyped": "^1.4.0" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/nuxt/node_modules/destr": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.1.tgz", "integrity": "sha512-M1Ob1zPSIvlARiJUkKqvAZ3VAqQY6Jcuth/pBKQ2b1dX/Qx0OnJ8Vux6J2H5PTMQeRzWrrbTu70VxBfv/OPDJA==", "dev": true }, + "node_modules/nuxt/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nuxt/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nuxt/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/nwsapi": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", diff --git a/package.json b/package.json index 67c1ac37cf..3abfeaecb6 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "test": "npm run vitest:run", "vitest:run": "nuxt generate test && vitest run", "vitest:dev": "nuxt generate test && vitest dev", - "start": "nuxt start --config-file playground/nuxt.config.js --target static", - "start:generate": "npm run generate && npm run start" + "start": "npx serve playground/dist", + "start:generate": "npm run generate && npx serve playground/dist" }, "exports": { ".": { @@ -69,7 +69,7 @@ "@commitlint/config-conventional": "17.7.0", "@fullhuman/postcss-purgecss": "5.0.0", "@nuxt/module-builder": "0.5.1", - "@nuxt/webpack-builder": "3.7.2", + "@nuxt/webpack-builder": "3.7.1", "@nuxtjs/eslint-config": "12.0.0", "@nuxtjs/eslint-module": "4.1.0", "@nuxtjs/stylelint-module": "5.1.0", @@ -90,7 +90,7 @@ "husky": "8.0.3", "jsdom": "22.1.0", "lint-staged": "14.0.1", - "nuxt": "3.7.2", + "nuxt": "^3.7.2", "pinst": "3.0.0", "playwright": "1.37.1", "postcss-functions": "4.0.2", diff --git a/src/module.mjs b/src/module.mjs index 843e7cde56..46c063ff4d 100644 --- a/src/module.mjs +++ b/src/module.mjs @@ -19,10 +19,7 @@ import { setPublicRuntimeConfig } from './utils.mjs'; import { getDefaultOptions } from './utils/options.mjs'; -import { - getFontConfigTemplate, - getFontConfigCSSTemplate -} from './utils/template.mjs'; +import { getFontConfigTemplate } from './utils/template.mjs'; import { optimizePreloads } from './utils/preload.mjs'; import { getSupportedBrowserDetector } from './utils/browser.mjs'; import { registerAppEntry as registerAppEntryWebpack } from './hookFunctions/webpack.mjs'; @@ -112,12 +109,6 @@ async function addBuildTemplates(nuxt, options) { write: true }); - addTemplate({ - filename: MODULE_NAME + '/fonts.mjs', - getContents: () => getFontConfigCSSTemplate(fontConfig), - write: true - }); - addTemplate({ filename: MODULE_NAME + '/fonts.css', getContents: () => fontConfig.toCSS(), diff --git a/src/runtime/classes/Font.mjs b/src/runtime/classes/Font.mjs index a44bc06e32..c1406bc3df 100644 --- a/src/runtime/classes/Font.mjs +++ b/src/runtime/classes/Font.mjs @@ -48,9 +48,9 @@ export default class Font { return btoa(JSON.stringify(data)); } - getCSSText() { + getCSSText({ usedFontaine = false } = {}) { const selector = createSelector(this.rootSelector, this.selector); - const family = `"${this.family}"`; + const family = prepareFamily(this.family, { usedFontaine }); return wrapByMedia( `${selector} { font-family: ${this.fallbackFamily.join(', ')}; @@ -64,9 +64,9 @@ export default class Font { ); } - getNoScriptCSSText() { + getNoScriptCSSText({ usedFontaine = false } = {}) { const selector = createSelector(this.rootSelector, this.selector); - const family = `"${this.family}"`; + const family = prepareFamily(this.family, { usedFontaine }); return wrapByMedia( `${selector} { font-family: ${[family].concat(this.fallbackFamily).join(', ')}; @@ -122,3 +122,10 @@ function weightNormalize(weight) { return weight; } } + +function prepareFamily(family, { usedFontaine = false } = {}) { + if (!usedFontaine) { + return `"${family}"`; + } + return `"${family}", "${family} fallback"`; +} diff --git a/src/runtime/classes/FontCollection.mjs b/src/runtime/classes/FontCollection.mjs index ef39d428f2..46c95eb02a 100644 --- a/src/runtime/classes/FontCollection.mjs +++ b/src/runtime/classes/FontCollection.mjs @@ -42,16 +42,18 @@ export default class FontCollection { ); } - getStyleDescriptions() { + getStyleDescriptions(options) { return getRelevantDescriptions([ - getStyleDescription(this.list.map(font => font.getCSSText()).join(' ')) + getStyleDescription( + this.list.map(font => font.getCSSText(options)).join(' ') + ) ]); } - getNoScriptStyleDescriptions() { + getNoScriptStyleDescriptions(options) { return getRelevantDescriptions([ getStyleDescription( - this.list.map(font => font.getNoScriptCSSText()).join(' '), + this.list.map(font => font.getNoScriptCSSText(options)).join(' '), true ) ]); diff --git a/src/runtime/composables/fonts.mjs b/src/runtime/composables/fonts.mjs index 9f21dd5c4f..9a7605ab63 100644 --- a/src/runtime/composables/fonts.mjs +++ b/src/runtime/composables/fonts.mjs @@ -14,7 +14,7 @@ export default function useFonts(context) { const fontCollection = reactive(new FontCollection()); - writeHead(isCritical, fontCollection); + writeHead(isCritical, fontCollection, runtimeConfig); return { isCritical, @@ -30,16 +30,17 @@ export default function useFonts(context) { }; } -function writeHead(isCritical, fontCollection) { +function writeHead(isCritical, fontCollection, runtimeConfig) { + const options = { usedFontaine: runtimeConfig.usedFontaine }; useHead({ link: computed(() => { return fontCollection.getPreloadDescriptions(isCritical.value); }), style: computed(() => { - return fontCollection.getStyleDescriptions(); + return fontCollection.getStyleDescriptions(options); }), noscript: computed(() => { - return fontCollection.getNoScriptStyleDescriptions(); + return fontCollection.getNoScriptStyleDescriptions(options); }) }); } diff --git a/src/runtime/tmpl/plugin.mjs b/src/runtime/tmpl/plugin.mjs index bec720418a..1b1c1c28b9 100644 --- a/src/runtime/tmpl/plugin.mjs +++ b/src/runtime/tmpl/plugin.mjs @@ -2,11 +2,13 @@ import vFont from '#speedkit/directives/vFont'; import { isSupportedBrowser } from '#speedkit/utils/browser'; import FontList from '#speedkit/classes/FontList'; import hydrate from '#speedkit/hydrate'; +import './fonts.css'; export default defineNuxtPlugin({ name: 'speedkit-plugin', enforce: 'post', async setup(nuxtApp) { + const fontConfig = await import('./fontConfig.mjs').then( module => module.default || module ); @@ -21,20 +23,6 @@ export default defineNuxtPlugin({ targetFormats: <%= JSON.stringify(options.targetFormats) %> }); - - const fonts = await import('./fonts.mjs').then( - module => module.default || module - ); - - useHead({ - style: [ - { - key: 'speedkit-fonts', - type: 'text/css', - children: fonts - } - ] - }); }, hooks: { 'app:created'() { diff --git a/src/utils.mjs b/src/utils.mjs index 6f7bb3d377..aff32166d2 100644 --- a/src/utils.mjs +++ b/src/utils.mjs @@ -24,7 +24,8 @@ export function isViteBuild(nuxt) { export const setPublicRuntimeConfig = (nuxt, options) => { nuxt.options.runtimeConfig.public.speedkit = { lazyOffsetComponent: options.lazyOffset.component, - lazyOffsetAsset: options.lazyOffset.asset + lazyOffsetAsset: options.lazyOffset.asset, + usedFontaine: !options.disableNuxtFontaine }; }; @@ -43,7 +44,8 @@ export async function addNuxtCritters(nuxt) { nuxt.options.critters = defu( { config: { - preload: false + preload: false, + fonts: false } }, nuxt.options.critters diff --git a/src/utils/options.mjs b/src/utils/options.mjs index 7cd93cab96..0ecf7f8c10 100644 --- a/src/utils/options.mjs +++ b/src/utils/options.mjs @@ -4,7 +4,7 @@ export function getDefaultOptions() { return { debug: false, - disableNuxtCritters: false, // If set, `@nuxtjs/critters` will not be integrated. + disableNuxtCritters: true, // If set, `@nuxtjs/critters` will not be integrated. disableNuxtFontaine: false, // If set, `@nuxtjs/fontaine` will not be integrated. disableNuxtImage: false, // If set, `@nuxt/image` will not be integrated. diff --git a/src/utils/preload.mjs b/src/utils/preload.mjs index 2b83bc3d0b..60e00fdbba 100644 --- a/src/utils/preload.mjs +++ b/src/utils/preload.mjs @@ -1,15 +1,20 @@ +import { promises as fsPromises } from 'fs'; +import { basename, dirname, join } from 'path'; import { parseDocument } from 'htmlparser2'; import { load } from 'cheerio'; import { render } from 'dom-serializer'; -import { isViteBuild } from '../utils.mjs'; +import { isViteBuild, logger } from '../utils.mjs'; export function optimizePreloads(nuxt) { if (isViteBuild(nuxt)) { nuxt.options.vite.build.manifest = false; + nuxt.options.vite.build.cssCodeSplit = false; } + nuxt.options.experimental.inlineSSRStyles = false; + nuxt.hook('nitro:init', nitro => { - nitro.hooks.hook('prerender:generate', route => { + nitro.hooks.hook('prerender:generate', async route => { if (!route.fileName?.endsWith('.html') || !route.contents) { return; } @@ -22,7 +27,38 @@ export function optimizePreloads(nuxt) { $('[rel="preload"][as="style"]').remove(); $('[rel="prefetch"][as="style"]').remove(); - $('link[rel="stylesheet"]').remove(); + + // embed css files + try { + await Promise.all( + Array.from($('link[rel="stylesheet"]')) + .map(el => $(el)) + .map(async $el => { + const dir = dirname($el.attr('href')); + + // https://nuxt.com/docs/guide/directory-structure/output + const publicDir = join( + nuxt.options.rootDir, + '.output/public', + nuxt.options.vite.build.assetsDir + ); + const filepath = join(publicDir, basename($el.attr('href'))); + + let css = await fsPromises.readFile(filepath, 'utf-8'); + logger.info( + `Embed CSS File \`${basename($el.attr('href'))}\`; Route: \`${ + route.route + }\`` + ); + + css = css.replace(/url\(.\//g, `url(${dir}/`); + $('head').append(``); + $el.remove(); + }) + ); + } catch (error) { + logger.error("can't embed css file.", error); + } route.contents = render(document); });