mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-29 00:52:01 +00:00
perf(nuxt): emit simpler functional ui templates (w/o lodash) (#27091)
This commit is contained in:
parent
a4d0958727
commit
68f4b193be
@ -8,11 +8,11 @@
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "pnpm --filter @nuxt/ui-templates prepack && pnpm --filter './packages/[^u]**' prepack",
|
||||
"build": "pnpm --filter './packages/**' prepack",
|
||||
"build:stub": "pnpm dev:prepare",
|
||||
"cleanup": "rimraf 'packages/**/node_modules' 'playground/node_modules' 'node_modules'",
|
||||
"dev": "pnpm play",
|
||||
"dev:prepare": "pnpm --filter @nuxt/ui-templates prepack && pnpm --filter './packages/[^u]**' prepack --stub",
|
||||
"dev:prepare": "pnpm --filter './packages/**' prepack --stub",
|
||||
"lint": "eslint . --cache",
|
||||
"lint:fix": "eslint . --cache --fix",
|
||||
"lint:docs": "markdownlint ./docs && case-police 'docs/**/*.md' *.md",
|
||||
@ -28,7 +28,7 @@
|
||||
"test:fixtures:webpack": "TEST_BUILDER=webpack pnpm test:fixtures",
|
||||
"test:runtime": "vitest -c vitest.nuxt.config.ts",
|
||||
"test:types": "pnpm --filter './test/fixtures/**' test:types",
|
||||
"test:unit": "JITI_CACHE=0 vitest run packages/",
|
||||
"test:unit": "vitest run packages/",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck:docs": "DOCS_TYPECHECK=true pnpm nuxi prepare && nuxt-content-twoslash verify --content-dir docs"
|
||||
},
|
||||
|
@ -1 +0,0 @@
|
||||
../../../../../ui-templates/dist/templates/error-500.d.ts
|
@ -1 +0,0 @@
|
||||
../../../../../ui-templates/dist/templates/error-500.js
|
1
packages/nuxt/src/core/runtime/nitro/error-500.ts
Symbolic link
1
packages/nuxt/src/core/runtime/nitro/error-500.ts
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../ui-templates/dist/templates/error-500.ts
|
@ -1 +0,0 @@
|
||||
../../../../../ui-templates/dist/templates/error-dev.d.ts
|
@ -1 +0,0 @@
|
||||
../../../../../ui-templates/dist/templates/error-dev.js
|
1
packages/nuxt/src/core/runtime/nitro/error-dev.ts
Symbolic link
1
packages/nuxt/src/core/runtime/nitro/error-dev.ts
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../ui-templates/dist/templates/error-dev.ts
|
@ -1,9 +1,9 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync, rmdirSync, unlinkSync, writeFileSync } from 'node:fs'
|
||||
import { basename, dirname, join, resolve } from 'pathe'
|
||||
import type { Plugin } from 'vite'
|
||||
// @ts-expect-error https://github.com/GoogleChromeLabs/critters/pull/151
|
||||
import Critters from 'critters'
|
||||
import { template } from 'lodash-es'
|
||||
import { genObjectFromRawEntries } from 'knitwork'
|
||||
import htmlMinifier from 'html-minifier'
|
||||
import { globby } from 'globby'
|
||||
@ -11,18 +11,20 @@ import { camelCase } from 'scule'
|
||||
|
||||
import genericMessages from '../templates/messages.json'
|
||||
|
||||
const r = (...path: string[]) => resolve(join(__dirname, '..', ...path))
|
||||
|
||||
const r = (path: string) => fileURLToPath(new URL(join('..', path), import.meta.url))
|
||||
const replaceAll = (input: string, search: string | RegExp, replace: string) => input.split(search).join(replace)
|
||||
|
||||
export const RenderPlugin = () => {
|
||||
let outputDir: string
|
||||
return <Plugin> {
|
||||
name: 'render',
|
||||
configResolved (config) {
|
||||
outputDir = r(config.build.outDir)
|
||||
},
|
||||
enforce: 'post',
|
||||
async writeBundle () {
|
||||
const distDir = r('dist')
|
||||
const critters = new Critters({ path: distDir })
|
||||
const htmlFiles = await globby(r('dist/templates/**/*.html'))
|
||||
const critters = new Critters({ path: outputDir })
|
||||
const htmlFiles = await globby(resolve(outputDir, 'templates/**/*.html'), { absolute: true })
|
||||
|
||||
const templateExports = []
|
||||
|
||||
@ -50,7 +52,7 @@ export const RenderPlugin = () => {
|
||||
.filter(src => src?.match(/\.svg$/))
|
||||
|
||||
for (const src of svgSources) {
|
||||
const svg = readFileSync(r('dist', src), 'utf-8')
|
||||
const svg = readFileSync(join(outputDir, src), 'utf-8')
|
||||
const base64Source = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
|
||||
html = replaceAll(html, src, base64Source)
|
||||
}
|
||||
@ -60,7 +62,7 @@ export const RenderPlugin = () => {
|
||||
.filter(([_block, src]) => src?.match(/^\/.*\.js$/))
|
||||
|
||||
for (const [scriptBlock, src] of scriptSources) {
|
||||
let contents = readFileSync(r('dist', src), 'utf-8')
|
||||
let contents = readFileSync(join(outputDir, src), 'utf-8')
|
||||
contents = replaceAll(contents, '/* empty css */', '').trim()
|
||||
html = html.replace(scriptBlock, contents.length ? `<script>${contents}</script>` : '')
|
||||
}
|
||||
@ -77,11 +79,22 @@ export const RenderPlugin = () => {
|
||||
const messages = JSON.parse(readFileSync(r(`templates/${templateName}/messages.json`), 'utf-8'))
|
||||
|
||||
// Serialize into a js function
|
||||
const jsCode = [
|
||||
const chunks = html.split(/\{{2,3}\s*[^{}]+\s*\}{2,3}/g).map(chunk => JSON.stringify(chunk))
|
||||
let templateString = chunks.shift()
|
||||
for (const expression of html.matchAll(/\{{2,3}(\s*[^{}]+\s*)\}{2,3}/g)) {
|
||||
templateString += ` + (${expression[1].trim()}) + ${chunks.shift()}`
|
||||
}
|
||||
if (chunks.length > 0) {
|
||||
templateString += ' + ' + chunks.join(' + ')
|
||||
}
|
||||
const functionalCode = [
|
||||
`export type DefaultMessages = Record<${Object.keys({ ...genericMessages, ...messages }).map(a => `"${a}"`).join(' | ') || 'string'}, string | boolean | number >`,
|
||||
`const _messages = ${JSON.stringify({ ...genericMessages, ...messages })}`,
|
||||
`const _render = ${template(html, { variable: '__var__', interpolate: /{{{?([\s\S]+?)}?}}/g }).toString().replace('__var__', '{ messages }')}`,
|
||||
'const _template = (messages) => _render({ messages: { ..._messages, ...messages } })',
|
||||
].join('\n').trim()
|
||||
'export const template = (messages: Partial<DefaultMessages>) => {',
|
||||
' messages = { ..._messages, ...messages }',
|
||||
` return ${templateString}`,
|
||||
'}',
|
||||
].join('\n')
|
||||
|
||||
const templateContent = html
|
||||
.match(/<body.*?>([\s\S]*)<\/body>/)?.[0]
|
||||
@ -146,20 +159,13 @@ export const RenderPlugin = () => {
|
||||
})
|
||||
|
||||
// Write new template
|
||||
writeFileSync(fileName.replace('/index.html', '.js'), `${jsCode}\nexport const template = _template`)
|
||||
writeFileSync(fileName.replace('/index.html', '.ts'), functionalCode)
|
||||
writeFileSync(fileName.replace('/index.html', '.vue'), vueCode)
|
||||
writeFileSync(fileName.replace('/index.html', '.d.ts'), `${types}`)
|
||||
|
||||
// Remove original html file
|
||||
unlinkSync(fileName)
|
||||
rmdirSync(dirname(fileName))
|
||||
}
|
||||
|
||||
// Write an index file with named exports for each template
|
||||
const contents = templateExports.map(exp => `export { template as ${exp.exportName} } from './templates/${exp.templateName}.js'`).join('\n')
|
||||
writeFileSync(r('dist/index.js'), contents, 'utf8')
|
||||
|
||||
writeFileSync(r('dist/index.d.ts'), replaceAll(contents, /\.js/g, ''), 'utf8')
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,12 @@
|
||||
"dev": "vite",
|
||||
"lint": "eslint --ext .ts,.js .",
|
||||
"optimize-assets": "npx svgo public/assets/**/*.svg",
|
||||
"prepack": "pnpm build",
|
||||
"postinstall": "pnpm build",
|
||||
"prerender": "pnpm build && jiti ./lib/prerender",
|
||||
"test": "pnpm lint && pnpm build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/html-minifier": "4.0.5",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@unocss/reset": "0.59.4",
|
||||
"critters": "0.0.22",
|
||||
"execa": "8.0.1",
|
||||
@ -28,7 +27,6 @@
|
||||
"html-minifier": "4.0.0",
|
||||
"jiti": "1.21.0",
|
||||
"knitwork": "1.1.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"pathe": "1.1.2",
|
||||
"prettier": "3.2.5",
|
||||
"scule": "1.3.0",
|
||||
|
@ -1,17 +1,22 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
import { rm } from 'node:fs/promises'
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||
import { execaCommand } from 'execa'
|
||||
import { format } from 'prettier'
|
||||
|
||||
const distDir = fileURLToPath(new URL('../dist/templates', import.meta.url))
|
||||
const distDir = fileURLToPath(new URL('../node_modules/.temp/dist/templates', import.meta.url))
|
||||
|
||||
describe('template', () => {
|
||||
beforeAll(async () => {
|
||||
await execaCommand('pnpm build', {
|
||||
cwd: fileURLToPath(new URL('..', import.meta.url)),
|
||||
env: {
|
||||
OUTPUT_DIR: './node_modules/.temp/dist',
|
||||
},
|
||||
})
|
||||
})
|
||||
afterAll(() => rm(distDir, { force: true, recursive: true }))
|
||||
|
||||
function formatCss (css: string) {
|
||||
return format(css, {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { join } from 'node:path'
|
||||
import { readdirSync } from 'node:fs'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
@ -7,10 +8,11 @@ import UnoCSS from 'unocss/vite'
|
||||
import { DevRenderingPlugin } from './lib/dev'
|
||||
import { RenderPlugin } from './lib/render'
|
||||
|
||||
const r = (...path: string[]) => resolve(__dirname, ...path)
|
||||
const r = (...path: string[]) => fileURLToPath(new URL(join(...path), import.meta.url))
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: process.env.OUTPUT_DIR || 'dist',
|
||||
rollupOptions: {
|
||||
input: {
|
||||
...Object.fromEntries(
|
||||
|
@ -557,9 +557,6 @@ importers:
|
||||
'@types/html-minifier':
|
||||
specifier: 4.0.5
|
||||
version: 4.0.5
|
||||
'@types/lodash-es':
|
||||
specifier: 4.17.12
|
||||
version: 4.17.12
|
||||
'@unocss/reset':
|
||||
specifier: 0.59.4
|
||||
version: 0.59.4
|
||||
@ -581,9 +578,6 @@ importers:
|
||||
knitwork:
|
||||
specifier: 1.1.0
|
||||
version: 1.1.0
|
||||
lodash-es:
|
||||
specifier: 4.17.21
|
||||
version: 4.17.21
|
||||
pathe:
|
||||
specifier: 1.1.2
|
||||
version: 1.1.2
|
||||
|
Loading…
Reference in New Issue
Block a user