mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-18 02:20:34 +03:00
feat: make ssr work
feat: update build script chore: use jiti instead of babel-node for examples
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
# Vue Meta Examples
|
||||
|
||||
## Prepare examples
|
||||
|
||||
To prepare the examples to run locally, please follow these steps:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/nuxt/vue-meta
|
||||
cd examples
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Run the examples
|
||||
|
||||
When the examples are installed locally, start the example server as follows
|
||||
|
||||
```js
|
||||
yarn start
|
||||
// or
|
||||
HOST=0.0.0.0 PORT=8080 yarn start
|
||||
```
|
||||
and browse to `http://localhost:3000` or whatever you changed the host and port to
|
||||
|
||||
### SSR Example
|
||||
|
||||
The server side rendering example is available on the cli only, to run the SSR example just run
|
||||
|
||||
```bash
|
||||
yarn ssr
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
If you would like to use the examples while developing or debugging `vue-meta` features or issues, please do as follows
|
||||
|
||||
```js
|
||||
git clone https://github.com/nuxt/vue-meta
|
||||
yarn install
|
||||
cd examples
|
||||
yarn install
|
||||
yarn dev
|
||||
```
|
||||
@@ -1,3 +1,8 @@
|
||||
if (!window.users) {
|
||||
window.users = []
|
||||
console.warn('window.users was not set')
|
||||
}
|
||||
|
||||
window.users.push({
|
||||
id: 1,
|
||||
name: 'Leanne Graham',
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
if (!window.users) {
|
||||
window.users = []
|
||||
console.warn('window.users was not set')
|
||||
}
|
||||
|
||||
window.users.push({
|
||||
id: 2,
|
||||
name: 'Ervin Howell',
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
if (!window.users) {
|
||||
window.users = []
|
||||
console.warn('window.users was not set')
|
||||
}
|
||||
|
||||
window.users.push({
|
||||
id: 3,
|
||||
name: 'Clementine Bauch',
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
if (!window.users) {
|
||||
window.users = []
|
||||
console.warn('window.users was not set')
|
||||
}
|
||||
|
||||
window.users.push({
|
||||
id: 4,
|
||||
name: 'Patricia Lebsack',
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
const path = require('path')
|
||||
const { transformSync } = require('@babel/core')
|
||||
|
||||
module.exports = require('jiti')(__filename, {
|
||||
cache: false,
|
||||
debug: false,
|
||||
transform (opts) {
|
||||
const _opts = {
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
compact: false,
|
||||
retainLines: typeof opts.retainLines === 'boolean' ? opts.retainLines : true,
|
||||
filename: '',
|
||||
cwd: '/',
|
||||
plugins: [
|
||||
[require('@babel/plugin-transform-modules-commonjs'), { allowTopLevelThis: true }],
|
||||
[require('@babel/plugin-transform-typescript')],
|
||||
[require('babel-plugin-dynamic-import-node'), { noInterop: true }],
|
||||
[require('babel-plugin-global-define'), {
|
||||
__DEV__: true,
|
||||
__BROWSER__: false
|
||||
}],
|
||||
[require('babel-plugin-module-resolver'), {
|
||||
root: '.',
|
||||
extensions: ['.ts'],
|
||||
alias: {
|
||||
'vue-meta': path.resolve(__dirname, '../src/')
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
|
||||
try {
|
||||
return transformSync(opts.source, _opts).code || ''
|
||||
} catch (err) {
|
||||
return 'exports.__JITI_ERROR__ = ' + JSON.stringify({
|
||||
filename: opts.filename,
|
||||
line: (err.loc && err.loc.line) || 0,
|
||||
column: (err.loc && err.loc.column) || 0,
|
||||
code: err.code && err.code.replace('BABEL_', '').replace('PARSE_ERROR', 'ParseError'),
|
||||
message: err.message.replace('/: ', '').replace(/\(.+\)\s*$/, '')
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,76 +0,0 @@
|
||||
const {
|
||||
baseParse,
|
||||
transform,
|
||||
generate,
|
||||
processIf,
|
||||
getBaseTransformPreset,
|
||||
createObjectExpression,
|
||||
createObjectProperty
|
||||
} = require('@vue/compiler-core')
|
||||
|
||||
const { parse } = require('@vue/compiler-dom')
|
||||
|
||||
function headTransform (node, context) {
|
||||
console.log('NODE', node)
|
||||
if (node.type === 1 /* NodeTypes.ELEMENT */) {
|
||||
return () => {
|
||||
if (!context.parent.codegenNode) {
|
||||
context.parent.codegenNode = createObjectExpression([])
|
||||
}
|
||||
|
||||
const options = context.parent.codegenNode
|
||||
const option = createObjectProperty(
|
||||
node.tag,
|
||||
node.children.length === 1 ? node.children[0] : 'null'
|
||||
)
|
||||
|
||||
// options.properties.push(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function (source, map) {
|
||||
// TODO: add options
|
||||
const ast = parse(source)
|
||||
// console.log('AST', ast)
|
||||
|
||||
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset({
|
||||
prefixIdentifiers: true
|
||||
})
|
||||
|
||||
transform(ast, {
|
||||
prefixIdentifiers: true,
|
||||
nodeTransforms: [...nodeTransforms, headTransform],
|
||||
directiveTransforms
|
||||
})
|
||||
|
||||
const result = generate(ast, { mode: 'module' })
|
||||
|
||||
console.log(result.code)
|
||||
|
||||
this.callback(
|
||||
null,
|
||||
`
|
||||
import { computed } from 'vue'
|
||||
|
||||
${result.code}
|
||||
export default function (component) {
|
||||
const setup = component.setup
|
||||
|
||||
component.setup = function (...args) {
|
||||
console.log(component)
|
||||
const __htmlMetaData = computed(() => {
|
||||
|
||||
})
|
||||
|
||||
return {
|
||||
...setup.apply(this, args),
|
||||
__htmlMetaData
|
||||
}
|
||||
}
|
||||
|
||||
}`,
|
||||
map
|
||||
)
|
||||
/**/
|
||||
}
|
||||
+13
-10
@@ -1,17 +1,20 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import consola from 'consola'
|
||||
import express from 'express'
|
||||
import rewrite from 'express-urlrewrite'
|
||||
import webpack from 'webpack'
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware'
|
||||
import WebpackConfig from './webpack.config'
|
||||
import { renderPage } from './ssr/server'
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const consola = require('consola')
|
||||
const express = require('express')
|
||||
const rewrite = require('express-urlrewrite')
|
||||
const webpack = require('webpack')
|
||||
const webpackDevMiddleware = require('webpack-dev-middleware')
|
||||
const webpackConfig = require('./webpack.config')
|
||||
const jiti = require('./jiti')
|
||||
|
||||
const { renderPage } = jiti('./ssr/server.js')
|
||||
// const { renderPage } = require('./ssr/server')
|
||||
|
||||
const app = express()
|
||||
|
||||
app.use(
|
||||
webpackDevMiddleware(webpack(WebpackConfig), {
|
||||
webpackDevMiddleware(webpack(webpackConfig(true)), {
|
||||
publicPath: '/__build__/',
|
||||
writeToDisk: true,
|
||||
stats: {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!doctype html>
|
||||
<html {{ htmlAttrs.text(true) }}>
|
||||
<head {{ headAttrs.text() }}>
|
||||
{{ head(true) }}
|
||||
<html {{ htmlAttrs }}>
|
||||
<head {{ headAttrs }}>
|
||||
{{ head }}
|
||||
<link rel="stylesheet" href="/global.css">
|
||||
</head>
|
||||
<body {{ bodyAttrs.text() }}>
|
||||
{{ bodyPrepend(true) }}
|
||||
<body {{ bodyAttrs }}>
|
||||
<body-prepend id="body-prepend">{{ bodyPrepend }}</body-prepend>
|
||||
|
||||
<a href="/">← Examples index</a>
|
||||
{{ app }}
|
||||
|
||||
<script src="/__build__/ssr.js"></script>
|
||||
{{ bodyAppend(true) }}
|
||||
{{ bodyAppend }}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+15
-24
@@ -1,8 +1,9 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import { createSSRApp } from 'vue'
|
||||
import { renderToStringWithMeta } from 'vue-meta'
|
||||
|
||||
import template from 'lodash/template'
|
||||
import { renderToString } from '@vue/server-renderer'
|
||||
import { App, createRouter, metaManager } from '../vue-router/main'
|
||||
|
||||
const templateFile = path.resolve(__dirname, 'app.template.html')
|
||||
@@ -15,41 +16,31 @@ process.server = true
|
||||
|
||||
export async function renderPage ({ url }) {
|
||||
console.log('renderPage', url)
|
||||
|
||||
const app = createSSRApp(App)
|
||||
const router = createRouter('/ssr', true)
|
||||
|
||||
app.use(router)
|
||||
// app.use(metaManager)
|
||||
app.use(metaManager)
|
||||
|
||||
console.log('renderPage', 'push')
|
||||
await router.push(url.substr(4))
|
||||
|
||||
await router.isReady()
|
||||
console.log('renderPage', 'eady')
|
||||
/* console.log(router)
|
||||
const matchedComponents = router.getMatchedComponents()
|
||||
// no matched routes, reject with 404
|
||||
if (!matchedComponents.length) {
|
||||
return reject({ code: 404 })
|
||||
} */
|
||||
|
||||
const appHtml = await renderToString(app)
|
||||
const [appHtml, ctx] = await renderToStringWithMeta(app)
|
||||
|
||||
if (!ctx.teleports) {
|
||||
ctx.teleports = {}
|
||||
}
|
||||
|
||||
const pageHtml = compiled({
|
||||
app: appHtml,
|
||||
htmlAttrs: {
|
||||
text: () => {}
|
||||
},
|
||||
headAttrs: {
|
||||
text: () => {}
|
||||
},
|
||||
bodyAttrs: {
|
||||
text: () => {}
|
||||
},
|
||||
head: () => {},
|
||||
bodyPrepend: () => {},
|
||||
bodyAppend: () => {}
|
||||
// ...app.$meta().inject()
|
||||
htmlAttrs: ctx.teleports.htmlAttrs || '',
|
||||
headAttrs: ctx.teleports.headAttrs || '',
|
||||
bodyAttrs: ctx.teleports.bodyAttrs || '',
|
||||
head: ctx.teleports.head || '',
|
||||
bodyPrepend: ctx.teleports['body-prepend'] || '',
|
||||
bodyAppend: ctx.teleports.body || ''
|
||||
})
|
||||
|
||||
return pageHtml
|
||||
|
||||
+14
-11
@@ -29,7 +29,7 @@ export default {
|
||||
{ tag: 'link', rel: 'stylesheet', href: 'style2.css' }
|
||||
]
|
||||
},
|
||||
body: 'body-script1.js',
|
||||
body: 'body-script1.js', // TODO: fix
|
||||
htmlAttrs: {
|
||||
amp: true,
|
||||
lang: ['en', 'nl']
|
||||
@@ -44,7 +44,7 @@ export default {
|
||||
// TODO { content: 'window.a = "<br/>"; </script><script>alert(\'asdasd\');' },
|
||||
// TODO { rawContent: 'window.b = "<br/>"; </script><script> alert(\'123321\');' },
|
||||
{ src: 'body-script2.js', to: 'body' },
|
||||
{ src: 'body-script3.js', to: '#put-it-here' }
|
||||
{ src: 'body-script3.js', to: 'body-prepend' }
|
||||
]
|
||||
/* esi: {
|
||||
children: [
|
||||
@@ -115,20 +115,15 @@ export default {
|
||||
}
|
||||
}
|
||||
setTimeout(() => walk(metadata), 1000) */
|
||||
|
||||
return {
|
||||
metadata
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<metainfo>
|
||||
<template v-slot:base="{ content, metainfo }">http://nuxt.dev:3000{{ content }}</template>
|
||||
/*
|
||||
<template v-slot:base="{ content, metainfo }">http://nuxt.dev:3000{{ content }}</template>
|
||||
<template v-slot:title="{ content, metainfo }">{{ content }} - {{ metainfo.description }} - Hello</template>
|
||||
<template v-slot:og(title)="{ content, metainfo, og }">
|
||||
{{ content }} - {{ og.description }} - {{ metainfo.description }} - Hello Again
|
||||
</template>
|
||||
|
||||
<!-- // TODO: Using script triggers [Vue warn]: Template compilation error: Tags with side effect (<script> and <style>) are ignored in client component templates. -->
|
||||
<!-- // TODO: Using script triggers [Vue warn]: Template compilation error: Tags with side effect (<script> and <style>) are i
|
||||
gnored in client component templates. -->
|
||||
<component is="script">window.users = []</component>
|
||||
<component is="script" src="user-1.js"></component>
|
||||
<component is="script" src="user-2.js"></component>
|
||||
@@ -147,6 +142,14 @@ export default {
|
||||
<template v-slot:body>
|
||||
<component is="script" src="user-4.js"></component>
|
||||
</template>
|
||||
*/
|
||||
return {
|
||||
metadata
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<metainfo>
|
||||
<template v-slot:body><br/></template>
|
||||
</metainfo>
|
||||
|
||||
<div id="app">
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body-prepend id="body-prepend"></body-prepend>
|
||||
<a href="/">← Examples index</a>
|
||||
<div id="app"></div>
|
||||
<div id="put-it-here"></div>
|
||||
<script src="/__build__/vue-router.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'vue'
|
||||
import { createRouter as createVueRouter, createMemoryHistory, createWebHistory } from 'vue-router'
|
||||
import { createManager, defaultConfig, resolveOption, useMeta } from 'vue-meta'
|
||||
import { createMetaManager, defaultConfig, resolveOption, useMeta } from 'vue-meta'
|
||||
import App from './App'
|
||||
import ChildComponent from './Child'
|
||||
|
||||
@@ -20,7 +20,7 @@ const decisionMaker5000000 = resolveOption((prevValue, context) => {
|
||||
}
|
||||
})
|
||||
|
||||
const metaManager = createManager({
|
||||
const metaManager = createMetaManager({
|
||||
...defaultConfig,
|
||||
esi: {
|
||||
group: true,
|
||||
|
||||
+81
-77
@@ -1,89 +1,93 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import webpack from 'webpack'
|
||||
import WebpackBar from 'webpackbar'
|
||||
import { VueLoaderPlugin } from 'vue-loader'
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const WebpackBar = require('webpackbar')
|
||||
const { VueLoaderPlugin } = require('vue-loader')
|
||||
|
||||
// const srcDir = path.join(__dirname, '..', 'src')
|
||||
|
||||
export default {
|
||||
devtool: 'inline-source-map',
|
||||
mode: 'development',
|
||||
entry: fs.readdirSync(__dirname)
|
||||
.reduce((entries, dir) => {
|
||||
const fullDir = path.join(__dirname, dir)
|
||||
module.exports = (isBrowser) => {
|
||||
const extraAliases = {}
|
||||
if (isBrowser) {
|
||||
extraAliases['./ssr$'] = path.resolve(__dirname, '../build/stub.js')
|
||||
}
|
||||
|
||||
if (dir === 'ssr') {
|
||||
entries[dir] = path.join(fullDir, 'browser.js')
|
||||
} else if (dir === 'vue-router') {
|
||||
const possibleEntries = ['browser', 'app']
|
||||
for (const entryName of possibleEntries) {
|
||||
const entry = path.join(fullDir, entryName + '.js')
|
||||
return {
|
||||
devtool: 'inline-source-map',
|
||||
mode: 'development',
|
||||
entry: fs.readdirSync(__dirname)
|
||||
.reduce((entries, dir) => {
|
||||
const fullDir = path.join(__dirname, dir)
|
||||
|
||||
if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
|
||||
entries[dir] = entry
|
||||
break
|
||||
if (dir === 'ssr') {
|
||||
entries[dir] = path.join(fullDir, 'browser.js')
|
||||
} else if (dir === 'vue-router') {
|
||||
const possibleEntries = ['browser', 'app']
|
||||
for (const entryName of possibleEntries) {
|
||||
const entry = path.join(fullDir, entryName + '.js')
|
||||
|
||||
if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
|
||||
entries[dir] = entry
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
}, {}),
|
||||
output: {
|
||||
path: path.join(__dirname, '__build__'),
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[id].chunk.js',
|
||||
publicPath: '/__build__/'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: 'babel-loader'
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: 'vue-loader'
|
||||
return entries
|
||||
}, {}),
|
||||
output: {
|
||||
path: path.join(__dirname, '__build__'),
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[id].chunk.js',
|
||||
publicPath: '/__build__/'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: 'vue-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', 'd.ts', '.ts', '.js', '.vue'],
|
||||
alias: {
|
||||
// this isn't technically needed, since the default `vue` entry for bundlers
|
||||
// is a simple `export * = require(''@vue/runtime-dom`. However having this
|
||||
// extra re-export somehow causes webpack to always invalidate the module
|
||||
// on the first HMR update and causes the page to reload.
|
||||
vue: 'vue/dist/vue.esm-bundler.js',
|
||||
'vue-meta': path.resolve(__dirname, '../src/'),
|
||||
...extraAliases
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', 'd.ts', '.ts', '.js', '.vue'],
|
||||
alias: {
|
||||
// this isn't technically needed, since the default `vue` entry for bundlers
|
||||
// is a simple `export * from '@vue/runtime-dom`. However having this
|
||||
// extra re-export somehow causes webpack to always invalidate the module
|
||||
// on the first HMR update and causes the page to reload.
|
||||
vue: 'vue/dist/vue.esm-bundler.js',
|
||||
'vue-meta': path.resolve(__dirname, '../src/')
|
||||
},
|
||||
// Expose __dirname to allow automatically setting basename.
|
||||
context: __dirname,
|
||||
node: {
|
||||
__dirname: true
|
||||
},
|
||||
plugins: [
|
||||
new WebpackBar(),
|
||||
new VueLoaderPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
|
||||
__DEV__: JSON.stringify(process.env.NODE_ENV !== 'production'),
|
||||
__BROWSER__: JSON.stringify(true),
|
||||
'process.client': JSON.stringify(true),
|
||||
'process.server': JSON.stringify(false)
|
||||
})
|
||||
],
|
||||
devServer: {
|
||||
inline: true,
|
||||
hot: true,
|
||||
stats: 'minimal',
|
||||
contentBase: __dirname,
|
||||
overlay: true
|
||||
}
|
||||
},
|
||||
// Expose __dirname to allow automatically setting basename.
|
||||
context: __dirname,
|
||||
node: {
|
||||
__dirname: true
|
||||
},
|
||||
plugins: [
|
||||
new WebpackBar(),
|
||||
new VueLoaderPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
|
||||
__DEV__: JSON.stringify(process.env.NODE_ENV !== 'production'),
|
||||
__VUE_OPTIONS_API__: JSON.stringify(true),
|
||||
__VUE_PROD_DEVTOOLS__: JSON.stringify(true)
|
||||
})
|
||||
],
|
||||
devServer: {
|
||||
inline: true,
|
||||
hot: true,
|
||||
stats: 'minimal',
|
||||
contentBase: __dirname,
|
||||
overlay: true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user