mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-24 01:00:33 +03:00
feat: make ssr work
feat: update build script chore: use jiti instead of babel-node for examples
This commit is contained in:
@@ -3,7 +3,8 @@
|
|||||||
"@nuxtjs/eslint-config-typescript"
|
"@nuxtjs/eslint-config-typescript"
|
||||||
],
|
],
|
||||||
"globals": {
|
"globals": {
|
||||||
"__DEV__": true
|
"__DEV__": true,
|
||||||
|
"__BROWSER__": false,
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-4
@@ -1,9 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016-2019
|
Copyright (c) 2021 - Pim (@pimlie)
|
||||||
- Declan de Wet
|
|
||||||
- Sébastien Chopin
|
|
||||||
- All the amazing contributors (https://github.com/nuxt/vue-meta/graphs/contributors)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
const path = require('path')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
['@babel/preset-env', {
|
|
||||||
useBuiltIns: 'usage',
|
|
||||||
corejs: 3,
|
|
||||||
targets: {
|
|
||||||
ie: 9
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
'@babel/preset-typescript',
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
'dynamic-import-node',
|
|
||||||
['global-define', {
|
|
||||||
'__DEV__': 'true'
|
|
||||||
}],
|
|
||||||
['module-resolver', {
|
|
||||||
root: '.',
|
|
||||||
extensions: ['.ts'],
|
|
||||||
alias: {
|
|
||||||
'vue-meta': path.resolve('./src/')
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
env: {
|
|
||||||
test: {
|
|
||||||
plugins: [
|
|
||||||
'@babel/plugin-syntax-dynamic-import',
|
|
||||||
'dynamic-import-node'
|
|
||||||
],
|
|
||||||
presets: [
|
|
||||||
[ '@babel/preset-env', {
|
|
||||||
targets: {
|
|
||||||
node: 'current'
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
'@babel/preset-typescript'
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import alias from '@rollup/plugin-alias'
|
||||||
|
// import babel from '@rollup/plugin-babel'
|
||||||
|
import commonjs from '@rollup/plugin-commonjs'
|
||||||
|
import nodeResolve from '@rollup/plugin-node-resolve'
|
||||||
|
import replace from '@rollup/plugin-replace'
|
||||||
|
import { terser } from 'rollup-plugin-terser'
|
||||||
|
import ts from 'rollup-plugin-typescript2'
|
||||||
|
import defaultsDeep from 'lodash/defaultsDeep'
|
||||||
|
|
||||||
|
const pkg = require('../package.json')
|
||||||
|
|
||||||
|
const banner = `/**
|
||||||
|
* ${pkg.name} v${pkg.version}
|
||||||
|
* (c) ${new Date().getFullYear()}
|
||||||
|
* - Pim (@pimlie)
|
||||||
|
* - All the amazing contributors
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
`
|
||||||
|
|
||||||
|
let didTS = false
|
||||||
|
|
||||||
|
function rollupConfig({
|
||||||
|
plugins = [],
|
||||||
|
external = [],
|
||||||
|
...config
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const isBrowserBuild = !config.output || !config.output.format || config.output.format === 'iife' || config.output.file.includes('-browser.')
|
||||||
|
const isProductionBuild = config.output.file.includes('.prod.')
|
||||||
|
|
||||||
|
const replaceConfig = {
|
||||||
|
exclude: 'node_modules',
|
||||||
|
delimiters: ['', ''],
|
||||||
|
values: {
|
||||||
|
'process.server' : isBrowserBuild ? 'false' : 'true', // should not be used anymore
|
||||||
|
'__DEV__': config.output.format === 'es' && !isBrowserBuild ? "(process.env.NODE_ENV !== 'production')" : !isProductionBuild,
|
||||||
|
'__BROWSER__': isBrowserBuild,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBrowserBuild) {
|
||||||
|
external = ['vue']
|
||||||
|
} else {
|
||||||
|
external = Object.keys(pkg.peerDependencies)
|
||||||
|
external.push('@vue/server-renderer')
|
||||||
|
}
|
||||||
|
|
||||||
|
const thisConfig = defaultsDeep({}, config, {
|
||||||
|
input: 'src/index.ts',
|
||||||
|
output: {
|
||||||
|
name: 'VueMeta',
|
||||||
|
format: 'iife',
|
||||||
|
sourcemap: false,
|
||||||
|
banner,
|
||||||
|
externalLiveBindings: false,
|
||||||
|
globals: {
|
||||||
|
vue: 'Vue'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
external,
|
||||||
|
plugins: [
|
||||||
|
replace(replaceConfig),
|
||||||
|
nodeResolve(),
|
||||||
|
commonjs(),
|
||||||
|
ts({
|
||||||
|
check: !didTS,
|
||||||
|
tsconfig: path.resolve(__dirname, '../tsconfig.json'),
|
||||||
|
cacheRoot: path.resolve(__dirname, '../node_modules/.rts2_cache'),
|
||||||
|
tsconfigOverride: {
|
||||||
|
compilerOptions: {
|
||||||
|
sourceMap: true,
|
||||||
|
declaration: !didTS,
|
||||||
|
declarationMap: !didTS,
|
||||||
|
},
|
||||||
|
exclude: ['__tests__', 'test-dts'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
].concat(plugins),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isBrowserBuild) {
|
||||||
|
// remove the ssr renderToString helper for browser builds
|
||||||
|
thisConfig.plugins.unshift(alias({
|
||||||
|
entries: [
|
||||||
|
{ find: '.\/ssr', replacement: path.resolve(__dirname, './stub.js') },
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.output.file.includes('.min.')) {
|
||||||
|
const terserOpts = {
|
||||||
|
module: config.output.format === 'es',
|
||||||
|
compress: {
|
||||||
|
ecma: 2015,
|
||||||
|
pure_getters: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
thisConfig.plugins.push(terser(terserOpts))
|
||||||
|
}
|
||||||
|
|
||||||
|
didTS = true
|
||||||
|
|
||||||
|
return thisConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
export default [
|
||||||
|
// umd web build
|
||||||
|
{
|
||||||
|
output: {
|
||||||
|
file: pkg.unpkg,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// minimized umd web build
|
||||||
|
{
|
||||||
|
output: {
|
||||||
|
file: pkg.unpkg.replace('.js', '.min.js'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// common js build
|
||||||
|
{
|
||||||
|
output: {
|
||||||
|
file: pkg.main,
|
||||||
|
format: 'cjs'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// common js build
|
||||||
|
{
|
||||||
|
output: {
|
||||||
|
file: pkg.main.replace('.js', '.prod.js'),
|
||||||
|
format: 'cjs'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// esm build
|
||||||
|
{
|
||||||
|
output: {
|
||||||
|
file: pkg.module,
|
||||||
|
format: 'es'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// browser esm build
|
||||||
|
{
|
||||||
|
output: {
|
||||||
|
file: pkg.module.replace('-bundler.js', '-browser.js'),
|
||||||
|
format: 'es'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// minimized browser esm build
|
||||||
|
{
|
||||||
|
output: {
|
||||||
|
file: pkg.module.replace('-bundler.js', '-browser.min.js'),
|
||||||
|
format: 'es'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
].map(rollupConfig)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -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({
|
window.users.push({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Leanne Graham',
|
name: 'Leanne Graham',
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
if (!window.users) {
|
||||||
|
window.users = []
|
||||||
|
console.warn('window.users was not set')
|
||||||
|
}
|
||||||
|
|
||||||
window.users.push({
|
window.users.push({
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Ervin Howell',
|
name: 'Ervin Howell',
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
if (!window.users) {
|
||||||
|
window.users = []
|
||||||
|
console.warn('window.users was not set')
|
||||||
|
}
|
||||||
|
|
||||||
window.users.push({
|
window.users.push({
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'Clementine Bauch',
|
name: 'Clementine Bauch',
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
if (!window.users) {
|
||||||
|
window.users = []
|
||||||
|
console.warn('window.users was not set')
|
||||||
|
}
|
||||||
|
|
||||||
window.users.push({
|
window.users.push({
|
||||||
id: 4,
|
id: 4,
|
||||||
name: 'Patricia Lebsack',
|
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'
|
const fs = require('fs')
|
||||||
import path from 'path'
|
const path = require('path')
|
||||||
import consola from 'consola'
|
const consola = require('consola')
|
||||||
import express from 'express'
|
const express = require('express')
|
||||||
import rewrite from 'express-urlrewrite'
|
const rewrite = require('express-urlrewrite')
|
||||||
import webpack from 'webpack'
|
const webpack = require('webpack')
|
||||||
import webpackDevMiddleware from 'webpack-dev-middleware'
|
const webpackDevMiddleware = require('webpack-dev-middleware')
|
||||||
import WebpackConfig from './webpack.config'
|
const webpackConfig = require('./webpack.config')
|
||||||
import { renderPage } from './ssr/server'
|
const jiti = require('./jiti')
|
||||||
|
|
||||||
|
const { renderPage } = jiti('./ssr/server.js')
|
||||||
|
// const { renderPage } = require('./ssr/server')
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
webpackDevMiddleware(webpack(WebpackConfig), {
|
webpackDevMiddleware(webpack(webpackConfig(true)), {
|
||||||
publicPath: '/__build__/',
|
publicPath: '/__build__/',
|
||||||
writeToDisk: true,
|
writeToDisk: true,
|
||||||
stats: {
|
stats: {
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html {{ htmlAttrs.text(true) }}>
|
<html {{ htmlAttrs }}>
|
||||||
<head {{ headAttrs.text() }}>
|
<head {{ headAttrs }}>
|
||||||
{{ head(true) }}
|
{{ head }}
|
||||||
<link rel="stylesheet" href="/global.css">
|
<link rel="stylesheet" href="/global.css">
|
||||||
</head>
|
</head>
|
||||||
<body {{ bodyAttrs.text() }}>
|
<body {{ bodyAttrs }}>
|
||||||
{{ bodyPrepend(true) }}
|
<body-prepend id="body-prepend">{{ bodyPrepend }}</body-prepend>
|
||||||
|
|
||||||
<a href="/">← Examples index</a>
|
<a href="/">← Examples index</a>
|
||||||
{{ app }}
|
{{ app }}
|
||||||
|
|
||||||
<script src="/__build__/ssr.js"></script>
|
<script src="/__build__/ssr.js"></script>
|
||||||
{{ bodyAppend(true) }}
|
{{ bodyAppend }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+15
-24
@@ -1,8 +1,9 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import { createSSRApp } from 'vue'
|
import { createSSRApp } from 'vue'
|
||||||
|
import { renderToStringWithMeta } from 'vue-meta'
|
||||||
|
|
||||||
import template from 'lodash/template'
|
import template from 'lodash/template'
|
||||||
import { renderToString } from '@vue/server-renderer'
|
|
||||||
import { App, createRouter, metaManager } from '../vue-router/main'
|
import { App, createRouter, metaManager } from '../vue-router/main'
|
||||||
|
|
||||||
const templateFile = path.resolve(__dirname, 'app.template.html')
|
const templateFile = path.resolve(__dirname, 'app.template.html')
|
||||||
@@ -15,41 +16,31 @@ process.server = true
|
|||||||
|
|
||||||
export async function renderPage ({ url }) {
|
export async function renderPage ({ url }) {
|
||||||
console.log('renderPage', url)
|
console.log('renderPage', url)
|
||||||
|
|
||||||
const app = createSSRApp(App)
|
const app = createSSRApp(App)
|
||||||
const router = createRouter('/ssr', true)
|
const router = createRouter('/ssr', true)
|
||||||
|
|
||||||
app.use(router)
|
app.use(router)
|
||||||
// app.use(metaManager)
|
app.use(metaManager)
|
||||||
|
|
||||||
console.log('renderPage', 'push')
|
|
||||||
await router.push(url.substr(4))
|
await router.push(url.substr(4))
|
||||||
|
|
||||||
await router.isReady()
|
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({
|
const pageHtml = compiled({
|
||||||
app: appHtml,
|
app: appHtml,
|
||||||
htmlAttrs: {
|
htmlAttrs: ctx.teleports.htmlAttrs || '',
|
||||||
text: () => {}
|
headAttrs: ctx.teleports.headAttrs || '',
|
||||||
},
|
bodyAttrs: ctx.teleports.bodyAttrs || '',
|
||||||
headAttrs: {
|
head: ctx.teleports.head || '',
|
||||||
text: () => {}
|
bodyPrepend: ctx.teleports['body-prepend'] || '',
|
||||||
},
|
bodyAppend: ctx.teleports.body || ''
|
||||||
bodyAttrs: {
|
|
||||||
text: () => {}
|
|
||||||
},
|
|
||||||
head: () => {},
|
|
||||||
bodyPrepend: () => {},
|
|
||||||
bodyAppend: () => {}
|
|
||||||
// ...app.$meta().inject()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return pageHtml
|
return pageHtml
|
||||||
|
|||||||
+14
-11
@@ -29,7 +29,7 @@ export default {
|
|||||||
{ tag: 'link', rel: 'stylesheet', href: 'style2.css' }
|
{ tag: 'link', rel: 'stylesheet', href: 'style2.css' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
body: 'body-script1.js',
|
body: 'body-script1.js', // TODO: fix
|
||||||
htmlAttrs: {
|
htmlAttrs: {
|
||||||
amp: true,
|
amp: true,
|
||||||
lang: ['en', 'nl']
|
lang: ['en', 'nl']
|
||||||
@@ -44,7 +44,7 @@ export default {
|
|||||||
// TODO { content: 'window.a = "<br/>"; </script><script>alert(\'asdasd\');' },
|
// TODO { content: 'window.a = "<br/>"; </script><script>alert(\'asdasd\');' },
|
||||||
// TODO { rawContent: 'window.b = "<br/>"; </script><script> alert(\'123321\');' },
|
// TODO { rawContent: 'window.b = "<br/>"; </script><script> alert(\'123321\');' },
|
||||||
{ src: 'body-script2.js', to: 'body' },
|
{ src: 'body-script2.js', to: 'body' },
|
||||||
{ src: 'body-script3.js', to: '#put-it-here' }
|
{ src: 'body-script3.js', to: 'body-prepend' }
|
||||||
]
|
]
|
||||||
/* esi: {
|
/* esi: {
|
||||||
children: [
|
children: [
|
||||||
@@ -115,20 +115,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTimeout(() => walk(metadata), 1000) */
|
setTimeout(() => walk(metadata), 1000) */
|
||||||
|
/*
|
||||||
return {
|
<template v-slot:base="{ content, metainfo }">http://nuxt.dev:3000{{ content }}</template>
|
||||||
metadata
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<metainfo>
|
|
||||||
<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:title="{ content, metainfo }">{{ content }} - {{ metainfo.description }} - Hello</template>
|
||||||
<template v-slot:og(title)="{ content, metainfo, og }">
|
<template v-slot:og(title)="{ content, metainfo, og }">
|
||||||
{{ content }} - {{ og.description }} - {{ metainfo.description }} - Hello Again
|
{{ content }} - {{ og.description }} - {{ metainfo.description }} - Hello Again
|
||||||
</template>
|
</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">window.users = []</component>
|
||||||
<component is="script" src="user-1.js"></component>
|
<component is="script" src="user-1.js"></component>
|
||||||
<component is="script" src="user-2.js"></component>
|
<component is="script" src="user-2.js"></component>
|
||||||
@@ -147,6 +142,14 @@ export default {
|
|||||||
<template v-slot:body>
|
<template v-slot:body>
|
||||||
<component is="script" src="user-4.js"></component>
|
<component is="script" src="user-4.js"></component>
|
||||||
</template>
|
</template>
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
metadata
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<metainfo>
|
||||||
|
<template v-slot:body><br/></template>
|
||||||
</metainfo>
|
</metainfo>
|
||||||
|
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<body-prepend id="body-prepend"></body-prepend>
|
||||||
<a href="/">← Examples index</a>
|
<a href="/">← Examples index</a>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<div id="put-it-here"></div>
|
|
||||||
<script src="/__build__/vue-router.js"></script>
|
<script src="/__build__/vue-router.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import { createRouter as createVueRouter, createMemoryHistory, createWebHistory } from 'vue-router'
|
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 App from './App'
|
||||||
import ChildComponent from './Child'
|
import ChildComponent from './Child'
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ const decisionMaker5000000 = resolveOption((prevValue, context) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const metaManager = createManager({
|
const metaManager = createMetaManager({
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
esi: {
|
esi: {
|
||||||
group: true,
|
group: true,
|
||||||
|
|||||||
+81
-77
@@ -1,89 +1,93 @@
|
|||||||
import fs from 'fs'
|
const fs = require('fs')
|
||||||
import path from 'path'
|
const path = require('path')
|
||||||
import webpack from 'webpack'
|
const webpack = require('webpack')
|
||||||
import WebpackBar from 'webpackbar'
|
const WebpackBar = require('webpackbar')
|
||||||
import { VueLoaderPlugin } from 'vue-loader'
|
const { VueLoaderPlugin } = require('vue-loader')
|
||||||
|
|
||||||
// const srcDir = path.join(__dirname, '..', 'src')
|
// const srcDir = path.join(__dirname, '..', 'src')
|
||||||
|
|
||||||
export default {
|
module.exports = (isBrowser) => {
|
||||||
devtool: 'inline-source-map',
|
const extraAliases = {}
|
||||||
mode: 'development',
|
if (isBrowser) {
|
||||||
entry: fs.readdirSync(__dirname)
|
extraAliases['./ssr$'] = path.resolve(__dirname, '../build/stub.js')
|
||||||
.reduce((entries, dir) => {
|
}
|
||||||
const fullDir = path.join(__dirname, dir)
|
|
||||||
|
|
||||||
if (dir === 'ssr') {
|
return {
|
||||||
entries[dir] = path.join(fullDir, 'browser.js')
|
devtool: 'inline-source-map',
|
||||||
} else if (dir === 'vue-router') {
|
mode: 'development',
|
||||||
const possibleEntries = ['browser', 'app']
|
entry: fs.readdirSync(__dirname)
|
||||||
for (const entryName of possibleEntries) {
|
.reduce((entries, dir) => {
|
||||||
const entry = path.join(fullDir, entryName + '.js')
|
const fullDir = path.join(__dirname, dir)
|
||||||
|
|
||||||
if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
|
if (dir === 'ssr') {
|
||||||
entries[dir] = entry
|
entries[dir] = path.join(fullDir, 'browser.js')
|
||||||
break
|
} 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
|
return entries
|
||||||
}, {}),
|
}, {}),
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, '__build__'),
|
path: path.join(__dirname, '__build__'),
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
chunkFilename: '[id].chunk.js',
|
chunkFilename: '[id].chunk.js',
|
||||||
publicPath: '/__build__/'
|
publicPath: '/__build__/'
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
use: 'ts-loader',
|
use: 'ts-loader',
|
||||||
exclude: /node_modules/
|
exclude: /node_modules/
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.vue$/,
|
||||||
exclude: /node_modules/,
|
use: 'vue-loader'
|
||||||
use: 'babel-loader'
|
}
|
||||||
},
|
]
|
||||||
{
|
},
|
||||||
test: /\.vue$/,
|
resolve: {
|
||||||
use: 'vue-loader'
|
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
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
},
|
// Expose __dirname to allow automatically setting basename.
|
||||||
resolve: {
|
context: __dirname,
|
||||||
extensions: ['.tsx', 'd.ts', '.ts', '.js', '.vue'],
|
node: {
|
||||||
alias: {
|
__dirname: true
|
||||||
// this isn't technically needed, since the default `vue` entry for bundlers
|
},
|
||||||
// is a simple `export * from '@vue/runtime-dom`. However having this
|
plugins: [
|
||||||
// extra re-export somehow causes webpack to always invalidate the module
|
new WebpackBar(),
|
||||||
// on the first HMR update and causes the page to reload.
|
new VueLoaderPlugin(),
|
||||||
vue: 'vue/dist/vue.esm-bundler.js',
|
new webpack.DefinePlugin({
|
||||||
'vue-meta': path.resolve(__dirname, '../src/')
|
'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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -1,5 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
testEnvironment: 'jest-environment-jsdom-global',
|
testEnvironment: 'jest-environment-jsdom-global',
|
||||||
|
preset: 'ts-jest',
|
||||||
|
|
||||||
expand: true,
|
expand: true,
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
globals: {
|
globals: {
|
||||||
__DEV__: true
|
__DEV__: true,
|
||||||
|
__BROWSER__: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-28
@@ -2,13 +2,16 @@
|
|||||||
"name": "vue-meta",
|
"name": "vue-meta",
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"description": "Manage HTML metadata in Vue.js components with SSR support",
|
"description": "Manage HTML metadata in Vue.js components with SSR support",
|
||||||
"main": "dist/vue-meta.common.js",
|
"main": "dist/vue-meta.cjs.js",
|
||||||
"web": "dist/vue-meta.js",
|
"unpkg": "dist/vue-meta.global.js",
|
||||||
"module": "dist/vue-meta.esm.js",
|
"jsdelivr": "dist/vue-meta.global.js",
|
||||||
"typings": "types/index.d.ts",
|
"module": "dist/vue-meta.esm-bundler.js",
|
||||||
|
"typings": "dist/vue-meta.d.ts",
|
||||||
|
"sideEffects": false,
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist/*.js",
|
||||||
"types/*.d.ts"
|
"types/*.d.ts",
|
||||||
|
"README.md"
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/nuxt/vue-meta",
|
"homepage": "https://github.com/nuxt/vue-meta",
|
||||||
"bugs": "https://github.com/nuxt/vue-meta/issues",
|
"bugs": "https://github.com/nuxt/vue-meta/issues",
|
||||||
@@ -30,12 +33,12 @@
|
|||||||
],
|
],
|
||||||
"author": "Pim (@pimlie)",
|
"author": "Pim (@pimlie)",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rimraf dist && rollup -c scripts/rollup.config.js",
|
"build": "yarn clean && rollup -c build/rollup.config.js",
|
||||||
|
"clean": "rimraf dist/*",
|
||||||
"coverage": "codecov",
|
"coverage": "codecov",
|
||||||
"dev": "babel-node examples/server.js",
|
"dev": "yarn clean && node examples/server.js",
|
||||||
"docs": "vuepress dev --host 0.0.0.0 --port 3000 docs",
|
"docs": "vuepress dev --host 0.0.0.0 --port 3000 docs",
|
||||||
"docs:build": "vuepress build docs",
|
"docs:build": "vuepress build docs",
|
||||||
"examples": "babel-node --extensions '.ts,.js' examples/server.js",
|
|
||||||
"lint": "eslint --ext .js,.ts src test examples",
|
"lint": "eslint --ext .js,.ts src test examples",
|
||||||
"prerelease": "git checkout master && git pull -r",
|
"prerelease": "git checkout master && git pull -r",
|
||||||
"release": "yarn lint && yarn test && standard-version",
|
"release": "yarn lint && yarn test && standard-version",
|
||||||
@@ -43,21 +46,25 @@
|
|||||||
"test:e2e-ssr": "jest test/e2e/ssr",
|
"test:e2e-ssr": "jest test/e2e/ssr",
|
||||||
"test:e2e-browser": "jest test/e2e/browser",
|
"test:e2e-browser": "jest test/e2e/browser",
|
||||||
"test:unit": "jest test/unit",
|
"test:unit": "jest test/unit",
|
||||||
"test:types": "tsc -p types/test"
|
"test:types": "tsc --build tsconfig.json"
|
||||||
},
|
},
|
||||||
"pperDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "next"
|
"@vue/server-renderer": "^3.0.5",
|
||||||
|
"vue": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.10",
|
"@babel/core": "^7.12.10",
|
||||||
"@babel/node": "^7.12.10",
|
"@babel/plugin-transform-modules-commonjs": "^7.12.1",
|
||||||
"@babel/preset-env": "^7.12.11",
|
|
||||||
"@babel/preset-typescript": "^7.12.7",
|
"@babel/preset-typescript": "^7.12.7",
|
||||||
"@nuxtjs/eslint-config-typescript": "^5.0.0",
|
"@nuxtjs/eslint-config-typescript": "^5.0.0",
|
||||||
|
"@rollup/plugin-alias": "^3.1.1",
|
||||||
|
"@rollup/plugin-commonjs": "^17.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^11.1.0",
|
||||||
|
"@rollup/plugin-replace": "^2.3.4",
|
||||||
"@types/webpack": "^4.41.26",
|
"@types/webpack": "^4.41.26",
|
||||||
"@types/webpack-env": "^1.16.0",
|
"@types/webpack-env": "^1.16.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.13.0",
|
"@typescript-eslint/eslint-plugin": "^4.14.0",
|
||||||
"@typescript-eslint/parser": "^4.13.0",
|
"@typescript-eslint/parser": "^4.14.0",
|
||||||
"@vue/compiler-sfc": "^3.0.5",
|
"@vue/compiler-sfc": "^3.0.5",
|
||||||
"@vue/server-renderer": "^3.0.5",
|
"@vue/server-renderer": "^3.0.5",
|
||||||
"@vue/server-test-utils": "^1.1.2",
|
"@vue/server-test-utils": "^1.1.2",
|
||||||
@@ -69,7 +76,7 @@
|
|||||||
"babel-plugin-global-define": "^1.0.3",
|
"babel-plugin-global-define": "^1.0.3",
|
||||||
"babel-plugin-module-resolver": "^4.1.0",
|
"babel-plugin-module-resolver": "^4.1.0",
|
||||||
"browserstack-local": "^1.4.8",
|
"browserstack-local": "^1.4.8",
|
||||||
"chromedriver": "^87.0.5",
|
"chromedriver": "^88.0.0",
|
||||||
"codecov": "^3.8.1",
|
"codecov": "^3.8.1",
|
||||||
"consola": "^2.15.0",
|
"consola": "^2.15.0",
|
||||||
"eslint": "^7.18.0",
|
"eslint": "^7.18.0",
|
||||||
@@ -79,17 +86,13 @@
|
|||||||
"jest": "^26.6.3",
|
"jest": "^26.6.3",
|
||||||
"jest-environment-jsdom": "^26.6.2",
|
"jest-environment-jsdom": "^26.6.2",
|
||||||
"jest-environment-jsdom-global": "^2.0.4",
|
"jest-environment-jsdom-global": "^2.0.4",
|
||||||
|
"jiti": "^1.3.0",
|
||||||
"jsdom": "^16.4.0",
|
"jsdom": "^16.4.0",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"node-env-file": "^0.1.8",
|
"node-env-file": "^0.1.8",
|
||||||
"puppeteer-core": "^5.5.0",
|
"puppeteer-core": "^5.5.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^2.36.2",
|
"rollup": "^2.38.0",
|
||||||
"rollup-plugin-babel": "^4.4.0",
|
|
||||||
"rollup-plugin-commonjs": "^10.1.0",
|
|
||||||
"rollup-plugin-json": "^4.0.0",
|
|
||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
|
||||||
"rollup-plugin-replace": "^2.2.0",
|
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"rollup-plugin-typescript2": "^0.29.0",
|
"rollup-plugin-typescript2": "^0.29.0",
|
||||||
"selenium-webdriver": "^4.0.0-alpha.8",
|
"selenium-webdriver": "^4.0.0-alpha.8",
|
||||||
@@ -97,15 +100,14 @@
|
|||||||
"tib": "^0.7.5",
|
"tib": "^0.7.5",
|
||||||
"ts-jest": "^26.4.4",
|
"ts-jest": "^26.4.4",
|
||||||
"ts-loader": "^8.0.14",
|
"ts-loader": "^8.0.14",
|
||||||
"ts-node": "^9.1.1",
|
|
||||||
"typescript": "^4.1.3",
|
"typescript": "^4.1.3",
|
||||||
"vue": "^3.0.0",
|
"vue": "^3.0.5",
|
||||||
"vue-jest": "^3.0.7",
|
"vue-jest": "^3.0.7",
|
||||||
"vue-loader": "^16.0.0",
|
"vue-loader": "^16.0.0",
|
||||||
"vue-router": "next",
|
"vue-router": "next",
|
||||||
"webpack": "^5.15.0",
|
"webpack": "^5.17.0",
|
||||||
"webpack-bundle-analyzer": "^4.3.0",
|
"webpack-bundle-analyzer": "^4.4.0",
|
||||||
"webpack-cli": "^4.3.1",
|
"webpack-cli": "^4.4.0",
|
||||||
"webpack-dev-server": "^3.11.2",
|
"webpack-dev-server": "^3.11.2",
|
||||||
"webpackbar": "^4.0.0"
|
"webpackbar": "^4.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,181 +0,0 @@
|
|||||||
import commonjs from 'rollup-plugin-commonjs'
|
|
||||||
import nodeResolve from 'rollup-plugin-node-resolve'
|
|
||||||
import json from 'rollup-plugin-json'
|
|
||||||
import babel from 'rollup-plugin-babel'
|
|
||||||
import replace from 'rollup-plugin-replace'
|
|
||||||
import { terser } from 'rollup-plugin-terser'
|
|
||||||
import defaultsDeep from 'lodash/defaultsDeep'
|
|
||||||
import { defaultOptions } from '../src/shared/constants'
|
|
||||||
|
|
||||||
const pkg = require('../package.json')
|
|
||||||
|
|
||||||
const version = pkg.version
|
|
||||||
|
|
||||||
const banner = `/**
|
|
||||||
* vue-meta v${version}
|
|
||||||
* (c) ${new Date().getFullYear()}
|
|
||||||
* - Declan de Wet
|
|
||||||
* - Sébastien Chopin (@Atinux)
|
|
||||||
* - Pim (@pimlie)
|
|
||||||
* - All the amazing contributors
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
`
|
|
||||||
|
|
||||||
const babelConfig = () => ({
|
|
||||||
presets: [
|
|
||||||
['@babel/preset-env', {
|
|
||||||
/*useBuiltIns: 'usage',
|
|
||||||
corejs: 2,*/
|
|
||||||
targets: {
|
|
||||||
node: 8,
|
|
||||||
ie: 9,
|
|
||||||
safari: '5.1'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const internalObjectProperties = [
|
|
||||||
// Plugin options
|
|
||||||
// NOTE, see shared/options for why/how this is possible to do
|
|
||||||
...Object.keys(defaultOptions),
|
|
||||||
'refreshOnceOnNavigation',
|
|
||||||
// Runtime state props on $root._vueMeta
|
|
||||||
'appId',
|
|
||||||
'pausing',
|
|
||||||
'navGuards',
|
|
||||||
'initialized',
|
|
||||||
'initializing',
|
|
||||||
'deprecationWarningShown',
|
|
||||||
// updateClientMetaInfo return props
|
|
||||||
'tagsAdded',
|
|
||||||
'tagsRemoved',
|
|
||||||
// escapeOptions
|
|
||||||
'doEscape',
|
|
||||||
// deepmerge
|
|
||||||
'isMergeableObject',
|
|
||||||
'arrayMerge'
|
|
||||||
]
|
|
||||||
|
|
||||||
const terserOpts = {
|
|
||||||
nameCache: {},
|
|
||||||
mangle: {
|
|
||||||
properties: {
|
|
||||||
//debug: '___DEBUGGGG___',
|
|
||||||
// minimize all object properties except when they are quotes like obj['prop']
|
|
||||||
keep_quoted: "strict",
|
|
||||||
// and minimize props listed in internalObjectProperties
|
|
||||||
regex: new RegExp(`^(${internalObjectProperties.join('|')})$`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function rollupConfig({
|
|
||||||
plugins = [],
|
|
||||||
...config
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const isBrowserBuild = !config.output || !config.output.format || config.output.format === 'umd' || config.output.file.includes('.browser.')
|
|
||||||
|
|
||||||
const replaceConfig = {
|
|
||||||
exclude: 'node_modules/(?!is-mergeable-object)',
|
|
||||||
delimiters: ['', ''],
|
|
||||||
values: {
|
|
||||||
// replaceConfig needs to have some values
|
|
||||||
'const polyfill = process.env.NODE_ENV === \'test\'': 'const polyfill = true',
|
|
||||||
'process.env.VERSION': `"${version}"`,
|
|
||||||
'process.server' : isBrowserBuild ? 'false' : 'true',
|
|
||||||
/* remove unused stuff from deepmerge */
|
|
||||||
// remove react stuff from is-mergeable-object
|
|
||||||
'|| isReactElement(value)': '|| false',
|
|
||||||
// we always provide an arrayMerge, remove default
|
|
||||||
'|| defaultArrayMerge' : '',
|
|
||||||
// clone is a deprecated option we dont use
|
|
||||||
'options.clone ' : 'false ',
|
|
||||||
// we dont provide a custom merge
|
|
||||||
'options.customMerge)' : 'false)',
|
|
||||||
// we dont use this helper
|
|
||||||
'deepmerge.all = ' : 'false;',
|
|
||||||
// we dont use symbols on our objects
|
|
||||||
'.concat(getEnumerableOwnPropertySymbols(target))': ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* / keep simple polyfills when babel plugin is used for build
|
|
||||||
if (plugins && plugins.some(p => p.name === 'babel')) {
|
|
||||||
replaceConfig.values = {
|
|
||||||
'const polyfill = process.env.NODE_ENV === \'test\'': 'const polyfill = true',
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return defaultsDeep({}, config, {
|
|
||||||
input: 'src/index.js',
|
|
||||||
output: {
|
|
||||||
name: 'VueMeta',
|
|
||||||
format: 'umd',
|
|
||||||
sourcemap: false,
|
|
||||||
banner
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
json(),
|
|
||||||
nodeResolve(),
|
|
||||||
replace(replaceConfig),
|
|
||||||
commonjs(),
|
|
||||||
babel(babelConfig()),
|
|
||||||
].concat(plugins),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default [
|
|
||||||
// umd web build
|
|
||||||
{
|
|
||||||
output: {
|
|
||||||
file: pkg.web,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// minimized umd web build
|
|
||||||
{
|
|
||||||
output: {
|
|
||||||
file: pkg.web.replace('.js', '.min.js'),
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
terser(terserOpts)
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// common js build
|
|
||||||
{
|
|
||||||
output: {
|
|
||||||
file: pkg.main,
|
|
||||||
format: 'cjs'
|
|
||||||
},
|
|
||||||
external: Object.keys(pkg.dependencies)
|
|
||||||
},
|
|
||||||
// esm build
|
|
||||||
{
|
|
||||||
output: {
|
|
||||||
file: pkg.web.replace('.js', '.esm.js'),
|
|
||||||
format: 'es'
|
|
||||||
},
|
|
||||||
external: Object.keys(pkg.dependencies)
|
|
||||||
},
|
|
||||||
// browser esm build
|
|
||||||
{
|
|
||||||
output: {
|
|
||||||
file: pkg.web.replace('.js', '.esm.browser.js'),
|
|
||||||
format: 'es'
|
|
||||||
},
|
|
||||||
external: Object.keys(pkg.dependencies)
|
|
||||||
},
|
|
||||||
// minimized browser esm build
|
|
||||||
{
|
|
||||||
output: {
|
|
||||||
file: pkg.web.replace('.js', '.esm.browser.min.js'),
|
|
||||||
format: 'es'
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
terser(terserOpts)
|
|
||||||
],
|
|
||||||
external: Object.keys(pkg.dependencies)
|
|
||||||
}
|
|
||||||
].map(rollupConfig)
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { readFileSync, writeFileSync } from 'fs'
|
|
||||||
import updateSection from 'update-section'
|
|
||||||
import { name, main, version } from '../package.json'
|
|
||||||
|
|
||||||
console.log(`Updating CDN info to latest v${version} release...`)
|
|
||||||
|
|
||||||
const readmePath = './README.md'
|
|
||||||
const cdnUrl = `https://unpkg.com/${name}@${version}/${main}`
|
|
||||||
const minifiedUrl = cdnUrl.replace('.js', '.min.js')
|
|
||||||
|
|
||||||
const content = readFileSync(readmePath, 'utf-8')
|
|
||||||
|
|
||||||
const update = `
|
|
||||||
<!-- start CDN generator - do **NOT** remove this comment -->
|
|
||||||
**Uncompressed:**
|
|
||||||
\`\`\`html
|
|
||||||
<script src="${cdnUrl}"></script>
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
**Minified:**
|
|
||||||
\`\`\`html
|
|
||||||
<script src="${minifiedUrl}"></script>
|
|
||||||
\`\`\`
|
|
||||||
<!-- end CDN generator - do **NOT** remove this comment -->
|
|
||||||
`.trim().replace(/ {2}/gm, '')
|
|
||||||
|
|
||||||
const updated = updateSection(
|
|
||||||
content,
|
|
||||||
update,
|
|
||||||
(line) => (/<!-- start CDN generator/).test(line),
|
|
||||||
(line) => (/<!-- end CDN generator/).test(line)
|
|
||||||
)
|
|
||||||
|
|
||||||
writeFileSync(readmePath, updated)
|
|
||||||
|
|
||||||
console.log('CDN info updated.')
|
|
||||||
+3
-59
@@ -1,78 +1,22 @@
|
|||||||
import { h, defineComponent, Teleport, VNode, VNodeProps } from 'vue'
|
import { defineComponent, VNodeProps } from 'vue'
|
||||||
import { isArray, isFunction } from '@vue/shared'
|
import { getCurrentManager } from './useApi'
|
||||||
import { renderMeta } from './render'
|
|
||||||
import { useMetainfo, getCurrentManager } from './useApi'
|
|
||||||
import { MetainfoActive } from './types'
|
import { MetainfoActive } from './types'
|
||||||
|
|
||||||
export interface MetainfoProps {
|
export interface MetainfoProps {
|
||||||
metainfo: MetainfoActive
|
metainfo: MetainfoActive
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addVnode (teleports: any, to: string, vnode: VNode | Array<VNode>) {
|
|
||||||
if (!teleports[to]) {
|
|
||||||
teleports[to] = []
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isArray(vnode)) {
|
|
||||||
teleports[to].push(...vnode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
teleports[to].push(vnode)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MetainfoImpl = defineComponent({
|
export const MetainfoImpl = defineComponent({
|
||||||
name: 'Metainfo',
|
name: 'Metainfo',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
setup (_, { slots }) {
|
setup (_, { slots }) {
|
||||||
const metainfo = useMetainfo()
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const teleports: any = {}
|
|
||||||
|
|
||||||
const manager = getCurrentManager()
|
const manager = getCurrentManager()
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key in metainfo) {
|
return manager.render({ slots })
|
||||||
const config = manager.config[key] || {}
|
|
||||||
|
|
||||||
const vnodes = renderMeta(
|
|
||||||
{ metainfo, slots },
|
|
||||||
key,
|
|
||||||
metainfo[key],
|
|
||||||
config
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!vnodes) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultTo =
|
|
||||||
(key !== 'base' && metainfo[key].to) || config.to || 'head'
|
|
||||||
|
|
||||||
if (isArray(vnodes)) {
|
|
||||||
for (const { to, vnode } of vnodes) {
|
|
||||||
addVnode(teleports, to || defaultTo, vnode)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const { to, vnode } = vnodes
|
|
||||||
addVnode(teleports, to || defaultTo, vnode)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const tag of ['default', 'head', 'body']) {
|
|
||||||
const slotFn = slots[tag]
|
|
||||||
if (isFunction(slotFn)) {
|
|
||||||
addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.keys(teleports).map((to) => {
|
|
||||||
return h(Teleport, { to }, teleports[to])
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Vendored
+1
@@ -1,2 +1,3 @@
|
|||||||
// Global compile-time constants
|
// Global compile-time constants
|
||||||
declare let __DEV__: boolean
|
declare let __DEV__: boolean
|
||||||
|
declare let __BROWSER__: boolean
|
||||||
|
|||||||
+10
-2
@@ -1,5 +1,13 @@
|
|||||||
|
import * as deepestResolver from './resolvers/deepest'
|
||||||
|
|
||||||
export { defaultConfig } from './config'
|
export { defaultConfig } from './config'
|
||||||
export { createManager } from './manager'
|
export { createMetaManager } from './manager'
|
||||||
export { resolveOption } from './resolvers'
|
export { resolveOption } from './resolvers'
|
||||||
export * from './useApi'
|
|
||||||
|
export * from './ssr'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
export * from './useApi'
|
||||||
|
|
||||||
|
export {
|
||||||
|
deepestResolver
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { App } from 'vue'
|
|
||||||
import { Metainfo } from './Metainfo'
|
|
||||||
import { metaInfoKey } from './symbols'
|
|
||||||
import { Manager } from './manager'
|
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
|
||||||
interface ComponentInternalInstance {
|
|
||||||
$metaManager: Manager
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function applyMetaPlugin (app: App, manager: Manager, active: Object) {
|
|
||||||
app.component('Metainfo', Metainfo)
|
|
||||||
|
|
||||||
app.config.globalProperties.$metaManager = manager
|
|
||||||
app.provide(metaInfoKey, active)
|
|
||||||
}
|
|
||||||
+99
-13
@@ -1,21 +1,46 @@
|
|||||||
import { App, reactive, onUnmounted, ComponentInternalInstance } from 'vue'
|
import { h, reactive, onUnmounted, Teleport, VNode, Comment } from 'vue'
|
||||||
import { isFunction } from '@vue/shared'
|
import { isArray, isFunction } from '@vue/shared'
|
||||||
import { createMergedObject } from './object-merge'
|
import { createMergedObject } from './object-merge'
|
||||||
import { applyMetaPlugin } from './install'
|
import { renderMeta } from './render'
|
||||||
// import * as deepestResolver from './resolvers/deepest'
|
import { metaInfoKey } from './symbols'
|
||||||
import { Config, Resolver, MetainfoInput, MetaContext, MetaProxy } from './types'
|
import { Metainfo } from './Metainfo'
|
||||||
import type { ResolveMethod } from './object-merge'
|
import type { ResolveMethod } from './object-merge'
|
||||||
|
import type { Manager, Config, Resolver, MetaContext, MetainfoActive } from './types'
|
||||||
|
|
||||||
export type Manager = {
|
export const ssrAttribute = 'data-vm-ssr'
|
||||||
readonly config: Config
|
|
||||||
|
|
||||||
install(app: App): void
|
export const active: MetainfoActive = reactive({})
|
||||||
addMeta(obj: MetainfoInput, vm?: ComponentInternalInstance): MetaProxy
|
|
||||||
|
export function addVnode (teleports: any, to: string, _vnodes: VNode | Array<VNode>) {
|
||||||
|
const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]) as Array<VNode>
|
||||||
|
|
||||||
|
if (!__BROWSER__) {
|
||||||
|
// dont add ssrAttribute for attribute vnode placeholder
|
||||||
|
if (!to.endsWith('Attrs')) {
|
||||||
|
vnodes.forEach((vnode) => {
|
||||||
|
if (!vnode.props) {
|
||||||
|
vnode.props = {}
|
||||||
|
}
|
||||||
|
vnode.props[ssrAttribute] = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Comments shouldnt have any use on the client as they are not reactive anyway
|
||||||
|
vnodes.forEach((vnode, idx) => {
|
||||||
|
if (vnode.type === Comment) {
|
||||||
|
vnodes.splice(idx, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!teleports[to]) {
|
||||||
|
teleports[to] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
teleports[to].push(...vnodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const active = reactive({})
|
export function createMetaManager (config: Config, resolver: Resolver | ResolveMethod): Manager {
|
||||||
|
|
||||||
export function createManager (config: Config, resolver: Resolver | ResolveMethod): Manager {
|
|
||||||
const resolve: ResolveMethod = (options, contexts, active, key, pathSegments) => {
|
const resolve: ResolveMethod = (options, contexts, active, key, pathSegments) => {
|
||||||
if (isFunction(resolver)) {
|
if (isFunction(resolver)) {
|
||||||
return resolver(options, contexts, active, key, pathSegments)
|
return resolver(options, contexts, active, key, pathSegments)
|
||||||
@@ -26,12 +51,17 @@ export function createManager (config: Config, resolver: Resolver | ResolveMetho
|
|||||||
|
|
||||||
const { addSource, delSource } = createMergedObject(resolve, active)
|
const { addSource, delSource } = createMergedObject(resolve, active)
|
||||||
|
|
||||||
|
let cleanedUpSsr = false
|
||||||
|
|
||||||
// TODO: validate resolver
|
// TODO: validate resolver
|
||||||
const manager: Manager = {
|
const manager: Manager = {
|
||||||
config,
|
config,
|
||||||
|
|
||||||
install (app) {
|
install (app) {
|
||||||
applyMetaPlugin(app, this, active)
|
app.component('Metainfo', Metainfo)
|
||||||
|
|
||||||
|
app.config.globalProperties.$metaManager = manager
|
||||||
|
app.provide(metaInfoKey, active)
|
||||||
},
|
},
|
||||||
|
|
||||||
addMeta (metaObj, vm) {
|
addMeta (metaObj, vm) {
|
||||||
@@ -52,6 +82,62 @@ export function createManager (config: Config, resolver: Resolver | ResolveMetho
|
|||||||
meta,
|
meta,
|
||||||
unmount
|
unmount
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render ({ slots }: any = {}): Array<VNode> {
|
||||||
|
// cleanup ssr tags if not yet done
|
||||||
|
if (__BROWSER__ && !cleanedUpSsr) {
|
||||||
|
cleanedUpSsr = true
|
||||||
|
|
||||||
|
// Listen for DOM loaded because tags in the body couldnt be loaded
|
||||||
|
// yet once the manager does it first render
|
||||||
|
// (preferable there should only be one render on hydration)
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`)
|
||||||
|
|
||||||
|
if (ssrTags && ssrTags.length) {
|
||||||
|
Array.from(ssrTags).forEach(el => el.parentNode && el.parentNode.removeChild(el))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const teleports: { [key: string]: VNode | Array<VNode> } = {}
|
||||||
|
|
||||||
|
for (const key in active) {
|
||||||
|
const config = this.config[key] || {}
|
||||||
|
|
||||||
|
const vnode = renderMeta(
|
||||||
|
{ metainfo: active, slots },
|
||||||
|
key,
|
||||||
|
active[key],
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!vnode) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const vnodes = isArray(vnode) ? vnode : [vnode]
|
||||||
|
|
||||||
|
const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head')
|
||||||
|
|
||||||
|
for (const { to, vnode } of vnodes) {
|
||||||
|
addVnode(teleports, to || defaultTo, vnode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slots) {
|
||||||
|
for (const tag in slots) {
|
||||||
|
const slotFn = slots[tag]
|
||||||
|
if (isFunction(slotFn)) {
|
||||||
|
addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(teleports).map((to) => {
|
||||||
|
return h(Teleport, { to }, teleports[to])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export type MergeSource = {
|
|||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
export type MergedObjectValue = boolean | number | string | MergedObject | any
|
export type MergedObjectValue = boolean | number | string | MergedObject | any
|
||||||
|
|
||||||
export type MergedObject = {
|
export type MergedObject = {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const createHandler: (context: MergeContext, resolveContext: ResolveConte
|
|||||||
},
|
},
|
||||||
set: (target, key, value) => {
|
set: (target, key, value) => {
|
||||||
const success = Reflect.set(target, key, value)
|
const success = Reflect.set(target, key, value)
|
||||||
console.warn(success, 'PROXY SET\nkey:', key, '\npath:', pathSegments, '\ntarget:', isArray(target), target, '\ncontext:\n', context)
|
// console.warn(success, 'PROXY SET\nkey:', key, '\npath:', pathSegments, '\ntarget:', isArray(target), target, '\ncontext:\n', context)
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
const isArrayItem = isArray(target)
|
const isArrayItem = isArray(target)
|
||||||
@@ -113,7 +113,7 @@ export const createHandler: (context: MergeContext, resolveContext: ResolveConte
|
|||||||
resolved = clone(resolved)
|
resolved = clone(resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
||||||
|
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved
|
active[activeSegmentKey] = resolved
|
||||||
@@ -122,12 +122,12 @@ export const createHandler: (context: MergeContext, resolveContext: ResolveConte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('CONTEXT.ACTIVE', context.active, '\nparent:\n', target)
|
// console.log('CONTEXT.ACTIVE', context.active, '\nparent:\n', target)
|
||||||
return success
|
return success
|
||||||
},
|
},
|
||||||
deleteProperty: (target, key) => {
|
deleteProperty: (target, key) => {
|
||||||
const success = Reflect.deleteProperty(target, key)
|
const success = Reflect.deleteProperty(target, key)
|
||||||
console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
const isArrayItem = isArray(target)
|
const isArrayItem = isArray(target)
|
||||||
@@ -174,7 +174,7 @@ export const createHandler: (context: MergeContext, resolveContext: ResolveConte
|
|||||||
resolved = clone(resolved)
|
resolved = clone(resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('SET VALUE', resolved)
|
// console.log('SET VALUE', resolved)
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved
|
active[activeSegmentKey] = resolved
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+26
-8
@@ -233,19 +233,37 @@ export function renderAttributes (
|
|||||||
key: string,
|
key: string,
|
||||||
data: TODO,
|
data: TODO,
|
||||||
config: TODO = {}
|
config: TODO = {}
|
||||||
): void {
|
): RenderedMetainfoNode | void {
|
||||||
// console.info('renderAttributes', key, data, config)
|
// console.info('renderAttributes', key, data, config)
|
||||||
|
|
||||||
const { attributesFor } = config
|
const { attributesFor } = config
|
||||||
|
|
||||||
if (!cachedElements[attributesFor]) {
|
if (!__BROWSER__) {
|
||||||
const el = document.querySelector(attributesFor)
|
// render attributes in a placeholder vnode so Vue
|
||||||
|
// will render the string for us
|
||||||
|
return {
|
||||||
|
to: '',
|
||||||
|
vnode: h(`ssr-${attributesFor}`, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (el) {
|
if (!cachedElements[attributesFor]) {
|
||||||
cachedElements[attributesFor] = {
|
const [el, el2] = Array.from(document.querySelectorAll(attributesFor))
|
||||||
el,
|
|
||||||
attrs: []
|
if (__DEV__ && !el) {
|
||||||
}
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Could not find element with selector', attributesFor, ', won\'t render attributes')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__DEV__ && el2) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn('Found multiple elements with selector', attributesFor)
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedElements[attributesFor] = {
|
||||||
|
el,
|
||||||
|
attrs: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
import type { App } from 'vue'
|
||||||
|
import type { SSRContext } from '@vue/server-renderer'
|
||||||
|
|
||||||
|
// rollup doesnt like an import, cant find export so use require
|
||||||
|
const { renderToString } = require('@vue/server-renderer')
|
||||||
|
|
||||||
|
export async function renderToStringWithMeta (app: App): Promise<[string, SSRContext]> {
|
||||||
|
const ctx: SSRContext = {}
|
||||||
|
|
||||||
|
const html = await renderToString(app, ctx)
|
||||||
|
|
||||||
|
// TODO: better way of determining whether meta was rendered with the component or not
|
||||||
|
if (!ctx.teleports || !ctx.teleports.head) {
|
||||||
|
const teleports = app.config.globalProperties.$metaManager.render()
|
||||||
|
await Promise.all(teleports.map((teleport: any) => renderToString(teleport, ctx)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const { teleports } = ctx
|
||||||
|
for (const target in teleports) {
|
||||||
|
if (target.endsWith('Attrs')) {
|
||||||
|
const str = teleports[target]
|
||||||
|
|
||||||
|
// match from first space to first >, these should be all rendered attributes
|
||||||
|
teleports[target] = str.slice(str.indexOf(' ') + 1, str.indexOf('>'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [html, ctx]
|
||||||
|
}
|
||||||
+25
-5
@@ -1,10 +1,6 @@
|
|||||||
import { ComponentInternalInstance } from 'vue'
|
import type { App, VNode, ComponentInternalInstance } from 'vue'
|
||||||
import type { MergedObject, ResolveContext, ResolveMethod } from '../object-merge'
|
import type { MergedObject, ResolveContext, ResolveMethod } from '../object-merge'
|
||||||
|
|
||||||
export type Immutable<T> = {
|
|
||||||
readonly [P in keyof T]: Immutable<T[P]>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TODO = any
|
export type TODO = any
|
||||||
|
|
||||||
export type MetainfoInput = {
|
export type MetainfoInput = {
|
||||||
@@ -50,3 +46,27 @@ export type Resolver = {
|
|||||||
setup?: ResolveSetup
|
setup?: ResolveSetup
|
||||||
resolve: ResolveMethod
|
resolve: ResolveMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Manager = {
|
||||||
|
readonly config: Config
|
||||||
|
|
||||||
|
install(app: App): void
|
||||||
|
addMeta(obj: MetainfoInput, vm?: ComponentInternalInstance): MetaProxy
|
||||||
|
|
||||||
|
render(ctx: { slots?: any }): Array<VNode>
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
interface ComponentInternalInstance {
|
||||||
|
$metaManager: Manager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace NodeJS {
|
||||||
|
interface Process {
|
||||||
|
client: boolean
|
||||||
|
server: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
import { inject, getCurrentInstance, ComponentInternalInstance } from 'vue'
|
import { inject, getCurrentInstance, ComponentInternalInstance } from 'vue'
|
||||||
import { Manager } from './manager'
|
|
||||||
import { metaInfoKey } from './symbols'
|
import { metaInfoKey } from './symbols'
|
||||||
import { MetainfoActive, MetainfoInput, MetaProxy } from './types'
|
import type { Manager, MetainfoActive, MetainfoInput, MetaProxy } from './types'
|
||||||
|
|
||||||
export function getCurrentManager (vm?: ComponentInternalInstance): Manager {
|
export function getCurrentManager (vm?: ComponentInternalInstance): Manager {
|
||||||
if (!vm) {
|
if (!vm) {
|
||||||
|
|||||||
@@ -478,10 +478,10 @@ describe('render', () => {
|
|||||||
const setAttribute = jest.fn()
|
const setAttribute = jest.fn()
|
||||||
const removeAttribute = jest.fn()
|
const removeAttribute = jest.fn()
|
||||||
|
|
||||||
const doc = jest.spyOn(document, 'querySelector').mockReturnValue({
|
const doc = jest.spyOn(document, 'querySelectorAll').mockReturnValue([{
|
||||||
setAttribute,
|
setAttribute,
|
||||||
removeAttribute
|
removeAttribute
|
||||||
})
|
}])
|
||||||
|
|
||||||
const context = {}
|
const context = {}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -30,6 +30,6 @@
|
|||||||
"include": [
|
"include": [
|
||||||
"src/global.d.ts",
|
"src/global.d.ts",
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
"__tests__/**/*.ts"
|
"test/**/*.ts"
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user