Skip to content

feat: check if a tag is already in window.customElements #9919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: dev
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/weex-vue-framework/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -5797,7 +5797,7 @@ function createPatchFunction (backend) {
}
}

function removeVnodes (parentElm, vnodes, startIdx, endIdx) {
function removeVnodes (vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
var ch = vnodes[startIdx];
if (isDef(ch)) {
Expand Down Expand Up @@ -5908,7 +5908,7 @@ function createPatchFunction (backend) {
refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
} else if (newStartIdx > newEndIdx) {
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
removeVnodes(oldCh, oldStartIdx, oldEndIdx);
}
}

Expand Down Expand Up @@ -5985,7 +5985,7 @@ function createPatchFunction (backend) {
if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
} else if (isDef(oldCh)) {
removeVnodes(elm, oldCh, 0, oldCh.length - 1);
removeVnodes(oldCh, 0, oldCh.length - 1);
} else if (isDef(oldVnode.text)) {
nodeOps.setTextContent(elm, '');
}
Expand Down Expand Up @@ -6216,7 +6216,7 @@ function createPatchFunction (backend) {

// destroy old node
if (isDef(parentElm$1)) {
removeVnodes(parentElm$1, [oldVnode], 0, 0);
removeVnodes([oldVnode], 0, 0);
} else if (isDef(oldVnode.tag)) {
invokeDestroyHook(oldVnode);
}
Expand Down
15 changes: 15 additions & 0 deletions src/compiler/error-detector.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ function checkNode (node: ASTNode, warn: Function) {
const range = node.rawAttrsMap[name]
if (name === 'v-for') {
checkFor(node, `v-for="${value}"`, warn, range)
} else if (name === 'v-slot' || name[0] === '#') {
checkFunctionParameterExpression(value, `${name}="${value}"`, warn, range)
} else if (onRE.test(name)) {
checkEvent(value, `${name}="${value}"`, warn, range)
} else {
Expand Down Expand Up @@ -111,3 +113,16 @@ function checkExpression (exp: string, text: string, warn: Function, range?: Ran
}
}
}

function checkFunctionParameterExpression (exp: string, text: string, warn: Function, range?: Range) {
try {
new Function(exp, '')
} catch (e) {
warn(
`invalid function parameter expression: ${e.message} in\n\n` +
` ${exp}\n\n` +
` Raw expression: ${text.trim()}\n`,
range
)
}
}
4 changes: 2 additions & 2 deletions src/compiler/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {

export const onRE = /^@|^v-on:/
export const dirRE = process.env.VBIND_PROP_SHORTHAND
? /^v-|^@|^:|^\./
: /^v-|^@|^:/
? /^v-|^@|^:|^\.|^#/
: /^v-|^@|^:|^#/
export const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
export const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
const stripParensRE = /^\(|\)$/g
Expand Down
3 changes: 1 addition & 2 deletions src/core/util/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,10 @@ if (typeof Set !== 'undefined' && isNative(Set)) {
}
}

interface SimpleSet {
export interface SimpleSet {
has(key: string | number): boolean;
add(key: string | number): mixed;
clear(): void;
}

export { _Set }
export type { SimpleSet }
6 changes: 6 additions & 0 deletions src/core/vdom/create-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ export function _createElement (
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
if (config.isReservedTag(tag)) {
// platform built-in elements
if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn)) {
warn(
`The .native modifier for v-on is only valid on components but it was used on <${tag}>.`,
context
)
}
vnode = new VNode(
config.parsePlatformTagName(tag), data, children,
undefined, undefined, context
Expand Down
12 changes: 8 additions & 4 deletions src/core/vdom/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { SSR_ATTR } from 'shared/constants'
import { registerRef } from './modules/ref'
import { traverse } from '../observer/traverse'
import { activeInstance } from '../instance/lifecycle'
import { inBrowser } from '../util/index';
import { isTextInputType } from 'web/util/element'

import {
Expand All @@ -32,6 +33,8 @@ export const emptyNode = new VNode('', {}, [])

const hooks = ['create', 'activate', 'update', 'remove', 'destroy']

const customElements = inBrowser ? window.customElements : undefined

function sameVnode (a, b) {
return (
a.key === b.key && (
Expand Down Expand Up @@ -106,6 +109,7 @@ export function createPatchFunction (backend) {

function isUnknownElement (vnode, inVPre) {
return (
!(customElements && customElements.get(vnode.tag)) &&
!inVPre &&
!vnode.ns &&
!(
Expand Down Expand Up @@ -358,7 +362,7 @@ export function createPatchFunction (backend) {
}
}

function removeVnodes (parentElm, vnodes, startIdx, endIdx) {
function removeVnodes (vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
const ch = vnodes[startIdx]
if (isDef(ch)) {
Expand Down Expand Up @@ -469,7 +473,7 @@ export function createPatchFunction (backend) {
refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
} else if (newStartIdx > newEndIdx) {
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
removeVnodes(oldCh, oldStartIdx, oldEndIdx)
}
}

Expand Down Expand Up @@ -561,7 +565,7 @@ export function createPatchFunction (backend) {
if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
} else if (isDef(oldCh)) {
removeVnodes(elm, oldCh, 0, oldCh.length - 1)
removeVnodes(oldCh, 0, oldCh.length - 1)
} else if (isDef(oldVnode.text)) {
nodeOps.setTextContent(elm, '')
}
Expand Down Expand Up @@ -790,7 +794,7 @@ export function createPatchFunction (backend) {

// destroy old node
if (isDef(parentElm)) {
removeVnodes(parentElm, [oldVnode], 0, 0)
removeVnodes([oldVnode], 0, 0)
} else if (isDef(oldVnode.tag)) {
invokeDestroyHook(oldVnode)
}
Expand Down
3 changes: 2 additions & 1 deletion test/e2e/nightwatch.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ module.exports = {
'desiredCapabilities': {
'browserName': 'phantomjs',
'javascriptEnabled': true,
'acceptSslCerts': true
'acceptSslCerts': true,
'phantomjs.binary.path': require('phantomjs-prebuilt').path
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions test/unit/features/component/component-scoped-slot.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,22 @@ describe('Component scoped slot', () => {
}).$mount()
expect(`Unexpected mixed usage of different slot syntaxes`).toHaveBeenWarned()
})

it('should warn invalid parameter expression', () => {
new Vue({
template: `<foo ${syntax}="1"></foo>`,
components: { Foo }
}).$mount();
expect('invalid function parameter expression').toHaveBeenWarned()
})

it('should allow destructuring props with default value', () => {
new Vue({
template: `<foo ${syntax}="{ foo = { bar: '1' } }"></foo>`,
components: { Foo }
}).$mount();
expect('invalid function parameter expression').not.toHaveBeenWarned()
})
}

// run tests for both full syntax and shorthand
Expand Down
14 changes: 14 additions & 0 deletions test/unit/features/directives/on.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,20 @@ describe('Directive v-on', () => {
expect(spy).toHaveBeenCalled()
})

it('should throw a warning if native modifier is used on native HTML element', () => {
vm = new Vue({
el,
template: `
<button @click.native="foo"></button>
`,
methods: { foo: spy },
})

triggerEvent(vm.$el, 'click')
expect(`The .native modifier for v-on is only valid on components but it was used on <button>.`).toHaveBeenWarned()
expect(spy.calls.count()).toBe(0)
})

it('.once modifier should work with child components', () => {
vm = new Vue({
el,
Expand Down
3 changes: 1 addition & 2 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Vue } from "./vue";
import "./umd";

export default Vue;

export as namespace Vue;

export {
CreateElement,
VueConstructor
Expand Down
24 changes: 16 additions & 8 deletions types/test/options-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,28 +76,36 @@ Vue.component('union-prop', {
complexUnion: { type: [User, Number] as PropType<User | number> },
kittyUser: Object as PropType<ICat & IUser>,
callback: Function as PropType<ConfirmCallback>,
mixed: [RegExp, Array],
object: [Cat, User],
primitive: [String, Number],
regex: RegExp,
union: [User, Number] as PropType<User | number>
},
data() {
this.cat;
this.complexUnion;
this.kittyUser;
this.callback(true);
this.mixed;
this.object;
this.primitive;
this.regex.compile;
this.union;
return {
fixedSize: this.union,
}
}
});

// stopped working since TS 3.4
// Vue.component('union-prop-with-no-casting', {
// props: {
// mixed: [RegExp, Array],
// object: [Cat, User],
// primitive: [String, Number],
// regex: RegExp
// },
// data() {
// this.mixed;
// this.object;
// this.primitive;
// this.regex.compile;
// }
// })

Vue.component('prop-with-primitive-default', {
props: {
id: {
Expand Down
14 changes: 3 additions & 11 deletions types/test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,9 @@
"vue": ["../index.d.ts"]
}
},
"files": [
"../index.d.ts",
"../options.d.ts",
"../plugin.d.ts",
"../vnode.d.ts",
"../vue.d.ts",
"options-test.ts",
"plugin-test.ts",
"vue-test.ts",
"augmentation-test.ts",
"ssr-test.ts"
"include": [
"../*.d.ts",
"*.ts"
],
"compileOnSave": false
}
7 changes: 7 additions & 0 deletions types/test/umd-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const vm = new Vue({
template: "<div>hi</div>"
});

const options: Vue.ComponentOptions<Vue> = {
template: "<div>test</div>"
};
48 changes: 48 additions & 0 deletions types/umd.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as V from "./index";
import {
DefaultData,
DefaultProps,
DefaultMethods,
DefaultComputed,
PropsDefinition
} from "./options";

// Expose some types for backword compatibility...
declare namespace Vue {
// vue.d.ts
export type CreateElement = V.CreateElement;
export type VueConstructor<V extends Vue = Vue> = V.VueConstructor<V>;

// options.d.ts
export type Component<Data=DefaultData<never>, Methods=DefaultMethods<never>, Computed=DefaultComputed, Props=DefaultProps> = V.Component<Data, Methods, Computed, Props>;
export type AsyncComponent<Data=DefaultData<never>, Methods=DefaultMethods<never>, Computed=DefaultComputed, Props=DefaultProps> = V.AsyncComponent<Data, Methods, Computed, Props>;
export type ComponentOptions<V extends Vue, Data=DefaultData<V>, Methods=DefaultMethods<V>, Computed=DefaultComputed, PropsDef=PropsDefinition<DefaultProps>, Props=DefaultProps> = V.ComponentOptions<V, Data, Methods, Computed, PropsDef, Props>;
export type FunctionalComponentOptions<Props = DefaultProps, PropDefs = PropsDefinition<Props>> = V.FunctionalComponentOptions<Props, PropDefs>;
export type RenderContext<Props=DefaultProps> = V.RenderContext<Props>;
export type PropType<T> = V.PropType<T>;
export type PropOptions<T=any> = V.PropOptions<T>;
export type ComputedOptions<T> = V.ComputedOptions<T>;
export type WatchHandler<T> = V.WatchHandler<T>;
export type WatchOptions = V.WatchOptions;
export type WatchOptionsWithHandler<T> = V.WatchOptionsWithHandler<T>;
export type DirectiveFunction = V.DirectiveFunction;
export type DirectiveOptions = V.DirectiveOptions;

// plugin.d.ts
export type PluginFunction<T> = V.PluginFunction<T>;
export type PluginObject<T> = V.PluginObject<T>;

// vnode.d.ts
export type VNodeChildren = V.VNodeChildren;
export type VNodeChildrenArrayContents = V.VNodeChildrenArrayContents;
export type VNode = V.VNode;
export type VNodeComponentOptions = V.VNodeComponentOptions;
export type VNodeData = V.VNodeData;
export type VNodeDirective = V.VNodeDirective;
}

declare class Vue extends V.default {}

export = Vue;

export as namespace Vue;