Skip to content

Commit

Permalink
feat: add support for nitro prerendering (nuxt 3 + bridge support) (#129
Browse files Browse the repository at this point in the history
)
  • Loading branch information
danielroe committed Aug 11, 2022
1 parent f5a41a3 commit fb99c9a
Show file tree
Hide file tree
Showing 22 changed files with 2,920 additions and 8,727 deletions.
File renamed without changes.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -7,3 +7,4 @@ node_modules
.DS_Store
coverage
dist
.output
21 changes: 11 additions & 10 deletions README.md
Expand Up @@ -13,6 +13,7 @@
- Zero-configuration required
- Enables CSS Extraction
- Critical CSS automatically injected to page
- Works with Nitro prerendering

## Quick setup

Expand All @@ -36,24 +37,27 @@ yarn add @nuxtjs/critters # or npm install @nuxtjs/critters

Nuxt has a number of ways to optimize your CSS in production:

1. ✅ Nuxt uses [`cssnano`](https://cssnano.co/) at the build step to minify CSS rules
1. ✅ Nuxt uses [`cssnano`](https://cssnano.co/) in the build step to minify CSS rules
2. 📦 You can enable [`purgecss`](https://github.com/Developmint/nuxt-purgecss) to remove unused CSS rules from your bundle.
3. ✅ with `@nuxtjs/critters` you can now extract CSS files and load them separately, just inlining the CSS necessary to render the page.

## Options

You can override the `@nuxtjs/critters` defaults like this:

```js
// nuxt.config.js
export default {
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: ['@nuxtjs/critters'],
critters: {
// Options passed directly to critters: https://github.com/GoogleChromeLabs/critters#critters-2
config: {
// Default: 'media'
preload: 'swap'
}
}
}
preload: 'swap',
},
},
})
```

## Development
Expand All @@ -67,17 +71,14 @@ export default {
[MIT License](./LICENSE)

<!-- Badges -->

[npm-version-src]: https://img.shields.io/npm/v/@nuxtjs/critters/latest.svg
[npm-version-href]: https://npmjs.com/package/@nuxtjs/critters

[npm-downloads-src]: https://img.shields.io/npm/dm/@nuxtjs/critters.svg
[npm-downloads-href]: https://npmjs.com/package/@nuxtjs/critters

[github-actions-ci-src]: https://github.com/nuxt-community/critters-module/workflows/ci/badge.svg
[github-actions-ci-href]: https://github.com/nuxt-community/critters-module/actions?query=workflow%3Aci

[codecov-src]: https://img.shields.io/codecov/c/github/nuxt-community/critters-module.svg
[codecov-href]: https://codecov.io/gh/nuxt-community/critters-module

[license-src]: https://img.shields.io/npm/l/@nuxtjs/critters.svg
[license-href]: https://npmjs.com/package/@nuxtjs/critters
4 changes: 0 additions & 4 deletions example/nuxt.config.js

This file was deleted.

3 changes: 0 additions & 3 deletions example/tsconfig.json

This file was deleted.

File renamed without changes.
56 changes: 29 additions & 27 deletions package.json
@@ -1,57 +1,59 @@
{
"name": "@nuxtjs/critters",
"version": "0.2.0",
"description": "Critical CSS for Nuxt.js",
"description": "Critical CSS for Nuxt",
"keywords": [
"performance",
"nuxt",
"module",
"nuxt-module"
],
"repository": "nuxt-community/critters-module",
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/module.mjs",
"require": "./dist/module.cjs"
}
},
"main": "./dist/module.cjs",
"types": "./dist/types.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "siroc build",
"dev": "nuxt example",
"prepack": "nuxt-module-build",
"dev": "nuxi dev playground",
"dev:build": "nuxi build playground",
"dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
"lint": "eslint --ext .js,.ts,.vue",
"lint:all": "yarn lint .",
"prepare": "husky install && yarn build",
"prepare": "husky install && yarn dev:prepare",
"release": "yarn test && release-it",
"test": "yarn lint && yarn jest"
"test": "yarn vitest run --no-threads"
},
"dependencies": {
"@nuxt/kit": "0.6.4",
"@nuxt/kit": "^3.0.0-rc.7",
"critters": "0.0.16",
"upath": "2.0.1"
"pathe": "^0.3.4"
},
"devDependencies": {
"@babel/plugin-transform-runtime": "7.18.10",
"@babel/preset-env": "7.18.10",
"@babel/preset-typescript": "7.18.6",
"@nuxt/test-utils": "0.2.2",
"@nuxt/types": "2.15.8",
"@nuxtjs/eslint-config-typescript": "10.0.0",
"@nuxt/module-builder": "latest",
"@nuxt/test-utils": "npm:@nuxt/test-utils-edge@latest",
"@nuxtjs/eslint-config-typescript": "latest",
"@release-it/conventional-changelog": "5.0.0",
"@types/jest": "28.1.6",
"babel-eslint": "latest",
"babel-jest": "28.1.3",
"eslint": "8.21.0",
"c8": "^7.12.0",
"eslint": "latest",
"eslint-config-prettier": "8.5.0",
"husky": "8.0.1",
"jest": "28.1.3",
"lint-staged": "13.0.3",
"nuxt-edge": "2.16.0-27616340.013f051b",
"release-it": "15.2.0",
"siroc": "0.16.0"
"nuxt": "npm:nuxt3@latest",
"release-it": "15.3.0",
"vitest": "^0.21.1"
},
"peerDependencies": {
"chalk": "^3.0.0 || ^4.0.0",
"consola": "^2.15.0",
"prettier": "^2.1.2"
"resolutions": {
"@nuxtjs/critters": "link:./"
},
"volta": {
"node": "16.16.0"
Expand Down
9 changes: 9 additions & 0 deletions playground/nuxt.config.js
@@ -0,0 +1,9 @@
export default {
modules: ['@nuxtjs/critters'],
css: ['~/styles/global.css'],
nitro: {
prerender: {
routes: ['/'],
},
},
}
3 changes: 3 additions & 0 deletions playground/package.json
@@ -0,0 +1,3 @@
{
"name": "nuxt-critters-playground"
}
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions playground/tsconfig.json
@@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}
File renamed without changes.
61 changes: 0 additions & 61 deletions src/index.ts

This file was deleted.

59 changes: 59 additions & 0 deletions src/module.ts
@@ -0,0 +1,59 @@
import { defineNuxtModule, isNuxt2 } from '@nuxt/kit'
import { resolve } from 'pathe'
import Critters, { Options } from 'critters'

export interface ModuleOptions {
// Options passed directly to `critters`
config?: Options
}

export default defineNuxtModule<ModuleOptions>({
meta: {
name: 'critters',
configKey: 'critters',
},
defaults: {
config: {
preload: 'media',
},
},
setup(options, nuxt) {
// Only enable for production
if (nuxt.options.dev) return

// Enable css extraction
nuxt.options.build.extractCSS = true

// Nitro handler (for prerendering only)

nuxt.hook('nitro:init', nitro => {
const critters = new Critters({
path: nitro.options.output.publicDir,
publicPath: nitro.options.baseURL,
...options.config,
})
nitro.hooks.hook('prerender:generate', async route => {
if (!route.contents || !route.fileName?.endsWith('.html')) return
route.contents = await critters.process(route.contents)
})
})

/* c8 ignore start */
if (isNuxt2()) {
const critters = new Critters({
path: resolve(nuxt.options.buildDir, 'dist/client'),
publicPath: nuxt.options.build.publicPath,
...options.config,
})

// Add transform step
nuxt.hook('render:route', async (_url, result) => {
result.html = await critters.process(result.html)
})

nuxt.hook('generate:page', async result => {
result.html = await critters.process(result.html)
})
}
},
})
18 changes: 18 additions & 0 deletions test/dev.spec.ts
@@ -0,0 +1,18 @@
import { fileURLToPath } from 'url'
import { setup, $fetch } from '@nuxt/test-utils'
import { describe, it, expect } from 'vitest'

await setup({
rootDir: fileURLToPath(new URL('../playground', import.meta.url).href),
server: true,
nuxtConfig: {
dev: true,
},
})

describe('module in dev mode', () => {
it('does not add module', async () => {
const body = await $fetch('/')
expect(body).not.toContain('<style>')
})
})
23 changes: 23 additions & 0 deletions test/generate.spec.ts
@@ -0,0 +1,23 @@
import { fileURLToPath } from 'url'
import { promises as fsp } from 'fs'
import { setup, useTestContext } from '@nuxt/test-utils'
import { resolve } from 'pathe'
import { describe, it, expect } from 'vitest'

await setup({
rootDir: fileURLToPath(new URL('../playground', import.meta.url).href),
build: true,
})

describe('module in generated pages', () => {
it('enables extractCSS', async () => {
const ctx = useTestContext()
const body = await fsp.readFile(
resolve(ctx.nuxt!.options.nitro.output?.dir || '', 'public/index.html'),
'utf-8'
)
expect(body).toContain('<style>')
expect(body).toContain('.sample-class')
expect(body).not.toContain('.sample-unused-class')
})
})

0 comments on commit fb99c9a

Please sign in to comment.