2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-22 09:10:34 +03:00

feat: add deepest resolver (wip)

feat: improve resolver related typing
This commit is contained in:
pimlie
2020-08-10 02:06:53 +02:00
parent 5add8bf83f
commit bb04dc068d
12 changed files with 926 additions and 666 deletions
+16 -1
View File
@@ -22,7 +22,7 @@ app.use(
) )
fs.readdirSync(__dirname) fs.readdirSync(__dirname)
.filter(file => file !== 'ssr') .filter(file => file[0] !== '_' && file !== 'ssr')
.forEach((file) => { .forEach((file) => {
if (fs.statSync(path.join(__dirname, file)).isDirectory()) { if (fs.statSync(path.join(__dirname, file)).isDirectory()) {
app.use(rewrite(`/${file}/*`, `/${file}/index.html`)) app.use(rewrite(`/${file}/*`, `/${file}/index.html`))
@@ -32,6 +32,21 @@ fs.readdirSync(__dirname)
app.use(express.static(path.join(__dirname, '_static'))) app.use(express.static(path.join(__dirname, '_static')))
app.use(express.static(__dirname)) app.use(express.static(__dirname))
app.use((req, res, next) => {
// Return empty css/javascript files if the file didnt exists
// statically. This means we can test/load any js file in
// the examples without errors in the browser
if (req.url.endsWith('.js')) {
res.setHeader('Content-Type', 'application/javascript')
res.send('')
next()
} else if (req.url.endsWith('.css')) {
res.setHeader('Content-Type', 'text/css')
res.send('')
next()
}
})
app.use(async (req, res, next) => { app.use(async (req, res, next) => {
if (!req.url.startsWith('/ssr')) { if (!req.url.startsWith('/ssr')) {
return next() return next()
+5 -6
View File
@@ -1,9 +1,7 @@
import { import {
createApp, createApp,
defineComponent, defineComponent,
getCurrentInstance,
reactive, reactive,
inject,
toRefs, toRefs,
h, h,
watch watch
@@ -20,10 +18,11 @@ const ChildComponent = defineComponent({
page: String page: String
}, },
template: ` template: `
<div> <div>
<h3>You're looking at the <strong>{{ page }}</strong> page</h3> <h3>You're looking at the <strong>{{ page }}</strong> page</h3>
<p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p> <p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p>
</div>`, </div>
`,
setup (props) { setup (props) {
const state = reactive({ const state = reactive({
date: null, date: null,
+20 -20
View File
@@ -48,59 +48,59 @@
"vue": "next" "vue": "next"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.10.5", "@babel/core": "^7.11.1",
"@babel/node": "^7.10.5", "@babel/node": "^7.10.5",
"@babel/preset-env": "^7.10.4", "@babel/preset-env": "^7.11.0",
"@babel/preset-typescript": "^7.10.4", "@babel/preset-typescript": "^7.10.4",
"@nuxtjs/eslint-config-typescript": "^2.0.0", "@nuxtjs/eslint-config-typescript": "^3.0.0",
"@types/webpack": "^4.41.21", "@types/webpack": "^4.41.21",
"@types/webpack-env": "^1.15.2", "@types/webpack-env": "^1.15.2",
"@typescript-eslint/eslint-plugin": "^3.7.0", "@typescript-eslint/eslint-plugin": "^3.8.0",
"@typescript-eslint/parser": "^3.7.0", "@typescript-eslint/parser": "^3.8.0",
"@vue/compiler-sfc": "^3.0.0-rc.4", "@vue/compiler-sfc": "^3.0.0-rc.5",
"@vue/server-renderer": "^3.0.0-rc.4", "@vue/server-renderer": "^3.0.0-rc.5",
"@vue/server-test-utils": "^1.0.3", "@vue/server-test-utils": "^1.0.3",
"@vue/test-utils": "^1.0.3", "@vue/test-utils": "^1.0.3",
"@wishy-gift/html-include-chunks-webpack-plugin": "^0.1.5", "@wishy-gift/html-include-chunks-webpack-plugin": "^0.1.5",
"babel-jest": "^26.1.0", "babel-jest": "^26.2.2",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-dynamic-import-node": "^2.3.3",
"browserstack-local": "^1.4.5", "browserstack-local": "^1.4.5",
"chromedriver": "^84.0.1", "chromedriver": "^84.0.1",
"codecov": "^3.7.2", "codecov": "^3.7.2",
"consola": "^2.14.0", "consola": "^2.15.0",
"eslint": "^7.5.0", "eslint": "^7.6.0",
"express-urlrewrite": "^1.3.0", "express-urlrewrite": "^1.3.0",
"geckodriver": "^1.19.1", "geckodriver": "^1.20.0",
"html-webpack-plugin": "^4.3.0", "html-webpack-plugin": "^4.3.0",
"jest": "^26.1.0", "jest": "^26.2.2",
"jest-environment-jsdom": "^26.1.0", "jest-environment-jsdom": "^26.2.0",
"jest-environment-jsdom-global": "^2.0.4", "jest-environment-jsdom-global": "^2.0.4",
"jsdom": "^16.3.0", "jsdom": "^16.4.0",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"node-env-file": "^0.1.8", "node-env-file": "^0.1.8",
"puppeteer-core": "^5.2.1", "puppeteer-core": "^5.2.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^2.23.0", "rollup": "^2.23.1",
"rollup-plugin-babel": "^4.4.0", "rollup-plugin-babel": "^4.4.0",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-json": "^4.0.0", "rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-replace": "^2.2.0", "rollup-plugin-replace": "^2.2.0",
"rollup-plugin-terser": "^6.1.0", "rollup-plugin-terser": "^7.0.0",
"rollup-plugin-typescript2": "^0.27.1", "rollup-plugin-typescript2": "^0.27.2",
"selenium-webdriver": "^4.0.0-alpha.7", "selenium-webdriver": "^4.0.0-alpha.7",
"standard-version": "^8.0.2", "standard-version": "^8.0.2",
"tib": "^0.7.4", "tib": "^0.7.4",
"ts-jest": "^26.1.3", "ts-jest": "^26.1.4",
"ts-loader": "^8.0.1", "ts-loader": "^8.0.2",
"ts-node": "^8.10.2", "ts-node": "^8.10.2",
"typescript": "^3.9.7", "typescript": "^3.9.7",
"vue": "next", "vue": "next",
"vue-jest": "^3.0.6", "vue-jest": "^3.0.6",
"vue-loader": "^16.0.0-beta.2", "vue-loader": "^16.0.0-beta.2",
"vue-router": "next", "vue-router": "next",
"webpack": "^4.44.0", "webpack": "^4.44.1",
"webpack-bundle-analyzer": "^3.8.0", "webpack-bundle-analyzer": "^3.8.0",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0", "webpack-dev-server": "^3.11.0",
+3 -3
View File
@@ -1,6 +1,6 @@
import { hasOwn } from '@vue/shared' import { hasOwn } from '@vue/shared'
import { clone } from '../utils' import { clone } from '../utils'
import { ActiveNode, MetaContext, PathSegments, ShadowNode } from '../types' import { ActiveNode, GetActiveNode, MetaContext, PathSegments, ShadowNode, GetShadowNodes } from '../types'
export function resolveActive ( export function resolveActive (
context: MetaContext, context: MetaContext,
@@ -13,8 +13,8 @@ export function resolveActive (
if (shadowParent[key].length > 1) { if (shadowParent[key].length > 1) {
// Is using freeze useful? Idea is to prevent the user from messing with these options by mistake // Is using freeze useful? Idea is to prevent the user from messing with these options by mistake
const getShadow = () => Object.freeze(clone(shadowParent[key])) const getShadow: GetShadowNodes = () => Object.freeze(clone(shadowParent[key]))
const getActive = () => Object.freeze(clone(activeParent[key])) const getActive: GetActiveNode = () => Object.freeze(clone(activeParent[key]))
value = context.manager.resolver.resolve( value = context.manager.resolver.resolve(
key, key,
+6 -5
View File
@@ -1,4 +1,4 @@
import { isPlainObject, hasOwn } from '@vue/shared' import { isPlainObject, /**/ hasOwn } from '@vue/shared'
import { ActiveNode, MetaContext, PathSegments, ShadowNode } from '../types' import { ActiveNode, MetaContext, PathSegments, ShadowNode } from '../types'
import { shadow, active } from './globals' import { shadow, active } from './globals'
import { resolveActive } from './resolve' import { resolveActive } from './resolve'
@@ -15,7 +15,7 @@ export function set (
// shadow & active should always be in sync // shadow & active should always be in sync
// if not we have bigger fish to fry // if not we have bigger fish to fry
if (!shadowParent[key]) { if (!shadowParent[key]) {
shadowParent[key] = {} shadowParent[key] = []
activeParent[key] = {} activeParent[key] = {}
} }
@@ -26,7 +26,7 @@ export function set (
activeParent[key], activeParent[key],
pathSegments pathSegments
) )
} }/**/
let idx = -1 let idx = -1
if (!shadowParent[key]) { if (!shadowParent[key]) {
@@ -34,7 +34,7 @@ export function set (
} else { } else {
// check if we already have a value listed for this element for this context // check if we already have a value listed for this element for this context
idx = shadowParent[key].findIndex( idx = shadowParent[key].findIndex(
({ context: $context }: { context: MetaContext }) => $context === context ({ context: shadowContext }: { context: MetaContext }) => shadowContext === context
) )
} }
@@ -68,12 +68,13 @@ export function setByObject (
} }
if (isPlainObject(shadowParent[key])) { if (isPlainObject(shadowParent[key])) {
console.log('HERERER')
setByObject(context, {}, shadowParent[key], activeParent[key], [ setByObject(context, {}, shadowParent[key], activeParent[key], [
...pathSegments, ...pathSegments,
key key
]) ])
continue continue
} } /**/
set(context, key, undefined, shadowParent, activeParent, [ set(context, key, undefined, shadowParent, activeParent, [
...pathSegments, ...pathSegments,
+4 -4
View File
@@ -1,8 +1,8 @@
import { App } from 'vue' import { App } from 'vue'
import { isFunction } from '@vue/shared' import { isFunction } from '@vue/shared'
import { applyMetaPlugin } from './install' import { applyMetaPlugin } from './install'
import * as deepestResolver from './resolvers/deepest' // import * as deepestResolver from './resolvers/deepest'
import { Config, ManagerResolverObject, ActiveResolverObject, MetaContext, PathSegments } from './types' import { Config, ManagerResolverObject, GetActiveNode, ActiveResolverObject, MetaContext, PathSegments, GetShadowNodes } from './types'
export type Manager = { export type Manager = {
readonly config: Config readonly config: Config
@@ -11,7 +11,7 @@ export type Manager = {
install(app: App): void install(app: App): void
} }
export function createManager (config: Config, resolver: ActiveResolverObject = deepestResolver): Manager { export function createManager (config: Config, resolver: ActiveResolverObject): Manager {
// TODO: validate resolver // TODO: validate resolver
const manager: Manager = { const manager: Manager = {
resolver: { resolver: {
@@ -22,7 +22,7 @@ export function createManager (config: Config, resolver: ActiveResolverObject =
resolver.setup(context) resolver.setup(context)
}, },
resolve (key: string, pathSegments: PathSegments, getShadow, getActive) { resolve (key: string, pathSegments: PathSegments, getShadow: GetShadowNodes, getActive: GetActiveNode) {
if (!resolver) { if (!resolver) {
return return
} }
+2 -8
View File
@@ -7,17 +7,11 @@ interface Target extends MetainfoInput {
__vm_proxy?: any // eslint-disable-line camelcase __vm_proxy?: any // eslint-disable-line camelcase
} }
export function createProxy ( export function createProxy (target: Target, handler: ProxyHandler<object>): Target {
target: Target,
handler: ProxyHandler<object>
): Target {
return markRaw(new Proxy(target, handler)) return markRaw(new Proxy(target, handler))
} }
export function createHandler ( export function createHandler (context: MetaContext, pathSegments: PathSegments = []): ProxyHandler<object> {
context: MetaContext,
pathSegments: PathSegments = []
): ProxyHandler<object> {
return { return {
get (target: object, key: string, receiver: object) { get (target: object, key: string, receiver: object) {
const value = Reflect.get(target, key, receiver) const value = Reflect.get(target, key, receiver)
+4 -4
View File
@@ -42,7 +42,7 @@ export function renderMeta (
data: TODO, data: TODO,
config: TODO config: TODO
): void | RenderedMetainfo | RenderedMetainfoNode { ): void | RenderedMetainfo | RenderedMetainfoNode {
console.info('renderMeta', key, data, config) // console.info('renderMeta', key, data, config)
if (config.attributesFor) { if (config.attributesFor) {
return renderAttributes(context, key, data, config) return renderAttributes(context, key, data, config)
@@ -61,7 +61,7 @@ export function renderGroup (
data: TODO, data: TODO,
config: TODO config: TODO
): RenderedMetainfo | RenderedMetainfoNode { ): RenderedMetainfo | RenderedMetainfoNode {
console.info('renderGroup', key, data, config) // console.info('renderGroup', key, data, config)
if (isArray(data)) { if (isArray(data)) {
if (__DEV__) { if (__DEV__) {
@@ -101,7 +101,7 @@ export function renderTag (
config: TODO = {}, config: TODO = {},
groupConfig?: GroupConfig groupConfig?: GroupConfig
): RenderedMetainfo | RenderedMetainfoNode { ): RenderedMetainfo | RenderedMetainfoNode {
console.info('renderTag', key, data, config, groupConfig) // console.info('renderTag', key, data, config, groupConfig)
const contentAttributes = ['content', 'json', 'rawContent'] const contentAttributes = ['content', 'json', 'rawContent']
const getConfig = (key: string) => getConfigByKey([tag, config.tag], key, config) const getConfig = (key: string) => getConfigByKey([tag, config.tag], key, config)
@@ -234,7 +234,7 @@ export function renderAttributes (
data: TODO, data: TODO,
config: TODO = {} config: TODO = {}
): void { ): void {
console.info('renderAttributes', key, data, config) // console.info('renderAttributes', key, data, config)
const { attributesFor } = config const { attributesFor } = config
+48 -7
View File
@@ -1,15 +1,56 @@
import { import {
ActiveNode, // ActiveNode,
/* ActiveResolverSetup, ActiveResolverMethod, */ MetaContext, /* ActiveResolverSetup, ActiveResolverMethod, */ MetaContext,
PathSegments, PathSegments,
ShadowNode ShadowNode,
GetActiveNode,
GetShadowNodes
} from '../types' } from '../types'
export function setup (context: MetaContext): void {} interface DeepestResolverMetaContext extends MetaContext {
depth?: number
}
export function setup (context: DeepestResolverMetaContext): void {
let depth: number = 0
if (context.vm) {
let { vm } = context
do {
depth++
vm = vm.parent
} while (vm && vm !== vm.root)
}
context.depth = depth
}
export function resolve ( export function resolve (
key: string, key: string,
pathSegments: PathSegments, _pathSegments: PathSegments,
shadow: ShadowNode, getOptions: GetShadowNodes,
active: ActiveNode getCurrentValue: GetActiveNode
): any {} ): any {
let resolvedOption: ShadowNode | void
const options = getOptions()
for (const option of options) {
if (!resolvedOption || resolvedOption.context.depth < option.context.depth) {
resolvedOption = option
}
}
console.log(
'DEEPEST.RESOLVE',
key,
getCurrentValue(),
options.map(({ value }) => value)
)
if (resolvedOption) {
console.log(resolvedOption.value)
return resolvedOption.value
}
}
+11 -3
View File
@@ -34,7 +34,7 @@ export interface MetainfoActive {
export type MetaContext = { export type MetaContext = {
id: string | symbol id: string | symbol
vm?: ComponentInternalInstance | null vm?: ComponentInternalInstance
manager: Manager manager: Manager
} }
@@ -42,8 +42,8 @@ export type ActiveResolverSetup = (context: MetaContext) => void
export type ActiveResolverMethod = ( export type ActiveResolverMethod = (
key: string, key: string,
pathSegments: PathSegments, pathSegments: PathSegments,
shadow: ShadowNode, shadow: GetShadowNodes,
active: ActiveNode active: GetActiveNode
) => any ) => any
export interface ActiveResolverObject { export interface ActiveResolverObject {
@@ -63,3 +63,11 @@ export interface ShadowNode {
export interface ActiveNode { export interface ActiveNode {
[key: string]: TODO [key: string]: TODO
} }
export interface GetShadowNodes {
(): Array<ShadowNode>
}
export interface GetActiveNode {
(): ActiveNode
}
+1 -1
View File
@@ -20,7 +20,7 @@ export function useMeta (obj: MetainfoInput, manager?: Manager) {
const context: MetaContext = { const context: MetaContext = {
id: PolySymbol(`context ${contextCounter++}`), id: PolySymbol(`context ${contextCounter++}`),
vm, vm: vm || undefined,
manager manager
} }
+806 -604
View File
File diff suppressed because it is too large Load Diff