Skip to content

Commit

Permalink
fix(webpack): added workaround for ssr styles
Browse files Browse the repository at this point in the history
  • Loading branch information
ThornWalli committed Nov 12, 2023
1 parent c90eed2 commit 663318d
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 72 deletions.
166 changes: 110 additions & 56 deletions src/hookFunctions/nitro/init.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,47 @@ import { basename, dirname, join, resolve } from 'pathe';
import { parseDocument } from 'htmlparser2';
import { load } from 'cheerio';
import { render } from 'dom-serializer';
import { logger } from '../../utils.mjs';
import { isWebpackBuild, logger } from '../../utils.mjs';

export default (nuxt, disableNuxtCritters = true) =>
export default (nuxt, options = {}) =>
nitro => {
nitro.hooks.hook('prerender:generate', async route => {
const { manifest, disableNuxtCritters } = {
disableNuxtCritters: true,
...options
};

if (!route.fileName?.endsWith('.html') || !route.contents) {
return;
}

// https://nuxt.com/docs/guide/directory-structure/output
const distNuxt = join(
nuxt.options.buildDir,
'dist/client',
nuxt.options.app.buildAssetsDir
);

const document = parseDocument(route.contents);
const $ = load(document);

// webpack workaround inline css in ssr
if (isWebpackBuild(nuxt)) {
await addedInlinedCSS($, getCSSFilesFromManifest($, manifest), {
distNuxt,
route
});
}

$('[rel="modulepreload"][as="script"]').remove();
$('[rel="prefetch"][as="script"]').remove();

$('[rel="preload"][as="fetch"]').remove();
$('[rel="preload"][as="style"]').remove();
$('[rel="prefetch"][as="style"]').remove();

// embed css files
try {
const css = await Promise.all(
Array.from($('link[rel="stylesheet"]'))
.map(el => $(el))
.filter($el => !isURL($el.attr('href')))
.map(async $el => {
const dir = dirname($el.attr('href'));

// https://nuxt.com/docs/guide/directory-structure/output
const distNuxt = join(
nuxt.options.buildDir,
'dist/client',
nuxt.options.app.buildAssetsDir
);
const filepath = join(distNuxt, basename($el.attr('href')));
const fileContent = await fsPromises.readFile(filepath, 'utf-8');

let urls = getUrlValues(fileContent);
urls = prepareUrls(urls, dir);

if (disableNuxtCritters) {
const css = urls.reduce(
(result, [a, b]) => result.replace(a, b),
fileContent
);

$el.remove();
logger.info(
`Embed CSS File \`${basename($el.attr('href'))}\`; Route: \`${
route.route
}\``
);
return css;
} else {
const matches = fileContent.match(
/\/\*! speedkit-font-faces start \*\/(.*)\/\*! speedkit-font-faces end \*\//
);
if (matches) {
logger.info(
`Embed Font-Faces CSS \`${basename(
$el.attr('href')
)}\`; Route: \`${route.route}\``
);
return matches[1].replace(/url\(.\//g, `url(${dir}/`);
}
}
})
);
if (css.length) {
$('head').append(`<style>${css.join('')}</style>`);
}
} catch (error) {
logger.error("can't embed css file.", error);
}
await prepareLinkStylesheets($, { disableNuxtCritters, distNuxt, route });

route.contents = render(document);
});
Expand Down Expand Up @@ -106,3 +74,89 @@ function isURL(value) {
function isDataURI(value) {
return value.startsWith('data:');
}

async function prepareLinkStylesheets(
$,
{ disableNuxtCritters, distNuxt, route }
) {
try {
const css = await Promise.all(
Array.from($('link[rel="stylesheet"]'))
.map(el => $(el))
.filter($el => !isURL($el.attr('href')))
.map(async $el => {
const dir = dirname($el.attr('href'));
const filepath = join(distNuxt, basename($el.attr('href')));
const fileContent = await fsPromises.readFile(filepath, 'utf-8');

Check warning on line 90 in src/hookFunctions/nitro/init.mjs

View workflow job for this annotation

GitHub Actions / Test & Build (ubuntu-latest, 19)

Found readFile from package "fs" with non literal argument at index 0

let urls = getUrlValues(fileContent);
urls = prepareUrls(urls, dir);

if (disableNuxtCritters) {
const css = urls.reduce(
(result, [a, b]) => result.replace(a, b),
fileContent
);

$el.remove();
logger.info(
`Embed CSS File \`${basename($el.attr('href'))}\`; Route: \`${
route.route
}\``
);
return css;
} else {
const matches = fileContent.match(
/\/\*! speedkit-font-faces start \*\/(.*)\/\*! speedkit-font-faces end \*\//
);
if (matches) {
logger.info(
`Embed Font-Faces CSS \`${basename(
$el.attr('href')
)}\`; Route: \`${route.route}\``
);
return matches[1].replace(/url\(.\//g, `url(${dir}/`);
}
}
})
);
if (css.length) {
$('head').append(`<style>${css.join('')}</style>`);
}
} catch (error) {
logger.error("can't embed css file.", error);
}
}

function getCSSFilesFromManifest($, manifest) {
const scripts = Array.from($('script[type="module"]'));
return scripts
.map(el => {
return (
manifest[String(`_${basename($(el).attr('src'))}`)]?.dynamicImports ||
[]
);
})
.flat()
.filter(file => file.endsWith('.css'))
.map(file => `${basename(file)}`);
}

async function addedInlinedCSS($, files, { distNuxt, route }) {
const css = await Promise.all(
files.map(async filename => {
// eslint-disable-next-line security/detect-non-literal-fs-filename
const css = await fsPromises.readFile(
join(distNuxt, 'css', filename),
'utf-8'
);
logger.info(
`Embed CSS File \`${basename(filename)}\`; Route: \`${route.route}\``
);
return css;
})
);
if (css.length) {
$('head').append(`<style>${css.join('')}</style>`);
}
}
7 changes: 0 additions & 7 deletions src/module.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,6 @@ export default defineNuxtModule({

setPublicRuntimeConfig(nuxt, moduleOptions);

// TODO: Remove in future
if (isWebpackBuild(nuxt)) {
logger.warn(
`[${MODULE_NAME}]: Webpack build is not usable yet.\nOpen Issues:\n- Inline Styles (\`https://nuxt.com/docs/api/configuration/nuxt-config#inlinessrstyles\`)`
);
}

if (moduleOptions.detection.performance && nuxt.options.ssr) {
if (isWebpackBuild(nuxt)) {
nuxt.hook(
Expand Down
6 changes: 3 additions & 3 deletions src/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function moduleExists(nuxt, moduleName) {
export async function addNuxtCritters(nuxt) {
if (!moduleExists(nuxt, '@nuxtjs/critters')) {
logger.info(
`[${MODULE_NAME}] added module \`@nuxtjs/critters\`, for more configuration learn more at \`https://github.com/nuxt-modules/critters\``
`Added module \`@nuxtjs/critters\`, for more configuration learn more at \`https://github.com/nuxt-modules/critters\``
);
nuxt.options.critters = defu(
{
Expand All @@ -58,7 +58,7 @@ export async function addNuxtCritters(nuxt) {
export async function addNuxtFontaine(nuxt) {
if (!moduleExists(nuxt, '@nuxtjs/fontaine')) {
logger.info(
`[${MODULE_NAME}] added module \`@nuxtjs/fontaine\`, for more configuration learn more at \`https://github.com/nuxt-modules/fontaine\``
`Added module \`@nuxtjs/fontaine\`, for more configuration learn more at \`https://github.com/nuxt-modules/fontaine\``
);
await installModule('@nuxtjs/fontaine');
}
Expand All @@ -67,7 +67,7 @@ export async function addNuxtFontaine(nuxt) {
export async function addNuxtImage(nuxt) {
if (!moduleExists(nuxt, '@nuxt/image')) {
logger.info(
`[${MODULE_NAME}] added module \`@nuxt/image\`, for more configuration learn more at \`https://image.nuxtjs.org/setup#configure\``
`Added module \`@nuxt/image\`, for more configuration learn more at \`https://image.nuxtjs.org/setup#configure\``
);
await installModule('@nuxt/image');
}
Expand Down
4 changes: 2 additions & 2 deletions src/utils/options.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MODULE_NAME, logger } from '../utils.mjs';
import { logger } from '../utils.mjs';

export function getDefaultOptions() {
return {
Expand Down Expand Up @@ -39,7 +39,7 @@ export function getDefaultOptions() {
export function deprecationsNotification(options) {
if ('loader' in options) {
logger.warn(
`[${MODULE_NAME}] Option \`loader\` is deprecated, There is no integrated loader anymore.`
`Option \`loader\` is deprecated, There is no integrated loader anymore.`
);
delete options.loader;
}
Expand Down
16 changes: 12 additions & 4 deletions src/utils/preload.mjs
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import initHook from '../hookFunctions/nitro/init.mjs';
import { isViteBuild } from '../utils.mjs';
import { isViteBuild, isWebpackBuild, logger } from '../utils.mjs';

export function optimizePreloads(moduleOptions, nuxt) {
const disableNuxtCritters = moduleOptions.disableNuxtCritters;
nuxt.options.experimental.inlineSSRStyles = false;

if (isViteBuild(nuxt)) {
nuxt.options.vite.build.manifest = false;
if (disableNuxtCritters) {
nuxt.options.vite.build.cssCodeSplit = false;
}
} else if (isWebpackBuild(nuxt)) {
nuxt.options.webpack.extractCSS = true;
logger.info(`Use workaround for \`SSR Styles\` in \`Webpack\`.`);
}

nuxt.options.experimental.inlineSSRStyles = false;

nuxt.hook('nitro:init', initHook(nuxt, disableNuxtCritters));
const hookOptions = {
disableNuxtCritters:
typeof disableNuxtCritters !== 'undefined' ? disableNuxtCritters : true,
manifest: null
};
nuxt.hook('build:manifest', manifest => (hookOptions.manifest = manifest));
nuxt.hook('nitro:init', initHook(nuxt, hookOptions));
}

0 comments on commit 663318d

Please sign in to comment.