mirror of https://github.com/vuejs/core.git
refactor: swap to new template parser
- get rid of SourceLocation.source for memory efficiency - move source location generation logic transform phase into the parser itself so that SourceLocation.source is no longer needed - move v-for expression parsing into the parser itself - added nameLoc on AttributeNode for use in transformElement Tests are not passing yet.
This commit is contained in:
parent
65b44045ef
commit
a60ad9180d
|
@ -40,6 +40,7 @@ import { PatchFlags } from '@vue/shared'
|
||||||
function createRoot(options: Partial<RootNode> = {}): RootNode {
|
function createRoot(options: Partial<RootNode> = {}): RootNode {
|
||||||
return {
|
return {
|
||||||
type: NodeTypes.ROOT,
|
type: NodeTypes.ROOT,
|
||||||
|
source: '',
|
||||||
children: [],
|
children: [],
|
||||||
helpers: new Set(),
|
helpers: new Set(),
|
||||||
components: [],
|
components: [],
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { ParserOptions } from '../src/options'
|
import { ParserOptions } from '../src/options'
|
||||||
import { TextModes } from '../src/parse'
|
|
||||||
import { ErrorCodes } from '../src/errors'
|
import { ErrorCodes } from '../src/errors'
|
||||||
import {
|
import {
|
||||||
CommentNode,
|
CommentNode,
|
||||||
|
@ -1913,35 +1912,38 @@ describe('compiler: parse', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test.skip('parse with correct location info', () => {
|
test.skip('parse with correct location info', () => {
|
||||||
|
const fooSrc = `foo
|
||||||
|
is `
|
||||||
|
const barSrc = `{{ bar }}`
|
||||||
|
const butSrc = ` but `
|
||||||
|
const bazSrc = `{{ baz }}`
|
||||||
const [foo, bar, but, baz] = baseParse(
|
const [foo, bar, but, baz] = baseParse(
|
||||||
`
|
fooSrc + barSrc + butSrc + bazSrc
|
||||||
foo
|
|
||||||
is {{ bar }} but {{ baz }}`.trim()
|
|
||||||
).children
|
).children
|
||||||
|
|
||||||
let offset = 0
|
let offset = 0
|
||||||
expect(foo.loc.start).toEqual({ line: 1, column: 1, offset })
|
expect(foo.loc.start).toEqual({ line: 1, column: 1, offset })
|
||||||
offset += foo.loc.source.length
|
offset += fooSrc.length
|
||||||
expect(foo.loc.end).toEqual({ line: 2, column: 5, offset })
|
expect(foo.loc.end).toEqual({ line: 2, column: 5, offset })
|
||||||
|
|
||||||
expect(bar.loc.start).toEqual({ line: 2, column: 5, offset })
|
expect(bar.loc.start).toEqual({ line: 2, column: 5, offset })
|
||||||
const barInner = (bar as InterpolationNode).content
|
const barInner = (bar as InterpolationNode).content
|
||||||
offset += 3
|
offset += 3
|
||||||
expect(barInner.loc.start).toEqual({ line: 2, column: 8, offset })
|
expect(barInner.loc.start).toEqual({ line: 2, column: 8, offset })
|
||||||
offset += barInner.loc.source.length
|
offset += 3
|
||||||
expect(barInner.loc.end).toEqual({ line: 2, column: 11, offset })
|
expect(barInner.loc.end).toEqual({ line: 2, column: 11, offset })
|
||||||
offset += 3
|
offset += 3
|
||||||
expect(bar.loc.end).toEqual({ line: 2, column: 14, offset })
|
expect(bar.loc.end).toEqual({ line: 2, column: 14, offset })
|
||||||
|
|
||||||
expect(but.loc.start).toEqual({ line: 2, column: 14, offset })
|
expect(but.loc.start).toEqual({ line: 2, column: 14, offset })
|
||||||
offset += but.loc.source.length
|
offset += butSrc.length
|
||||||
expect(but.loc.end).toEqual({ line: 2, column: 19, offset })
|
expect(but.loc.end).toEqual({ line: 2, column: 19, offset })
|
||||||
|
|
||||||
expect(baz.loc.start).toEqual({ line: 2, column: 19, offset })
|
expect(baz.loc.start).toEqual({ line: 2, column: 19, offset })
|
||||||
const bazInner = (baz as InterpolationNode).content
|
const bazInner = (baz as InterpolationNode).content
|
||||||
offset += 3
|
offset += 3
|
||||||
expect(bazInner.loc.start).toEqual({ line: 2, column: 22, offset })
|
expect(bazInner.loc.start).toEqual({ line: 2, column: 22, offset })
|
||||||
offset += bazInner.loc.source.length
|
offset += 3
|
||||||
expect(bazInner.loc.end).toEqual({ line: 2, column: 25, offset })
|
expect(bazInner.loc.end).toEqual({ line: 2, column: 25, offset })
|
||||||
offset += 3
|
offset += 3
|
||||||
expect(baz.loc.end).toEqual({ line: 2, column: 28, offset })
|
expect(baz.loc.end).toEqual({ line: 2, column: 28, offset })
|
||||||
|
@ -2073,8 +2075,7 @@ foo
|
||||||
|
|
||||||
test.skip('should NOT condense whitespaces in RCDATA text mode', () => {
|
test.skip('should NOT condense whitespaces in RCDATA text mode', () => {
|
||||||
const ast = baseParse(`<textarea>Text:\n foo</textarea>`, {
|
const ast = baseParse(`<textarea>Text:\n foo</textarea>`, {
|
||||||
getTextMode: ({ tag }) =>
|
parseMode: 'html'
|
||||||
tag === 'textarea' ? TextModes.RCDATA : TextModes.DATA
|
|
||||||
})
|
})
|
||||||
const preElement = ast.children[0] as ElementNode
|
const preElement = ast.children[0] as ElementNode
|
||||||
expect(preElement.children).toHaveLength(1)
|
expect(preElement.children).toHaveLength(1)
|
||||||
|
@ -3069,24 +3070,7 @@ foo
|
||||||
() => {
|
() => {
|
||||||
const spy = vi.fn()
|
const spy = vi.fn()
|
||||||
const ast = baseParse(code, {
|
const ast = baseParse(code, {
|
||||||
getNamespace: (tag, parent) => {
|
parseMode: 'html',
|
||||||
const ns = parent ? parent.ns : Namespaces.HTML
|
|
||||||
if (ns === Namespaces.HTML) {
|
|
||||||
if (tag === 'svg') {
|
|
||||||
return (Namespaces.HTML + 1) as any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ns
|
|
||||||
},
|
|
||||||
getTextMode: ({ tag }) => {
|
|
||||||
if (tag === 'textarea') {
|
|
||||||
return TextModes.RCDATA
|
|
||||||
}
|
|
||||||
if (tag === 'script') {
|
|
||||||
return TextModes.RAWTEXT
|
|
||||||
}
|
|
||||||
return TextModes.DATA
|
|
||||||
},
|
|
||||||
...options,
|
...options,
|
||||||
onError: spy
|
onError: spy
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { baseParse } from '../src/parse'
|
import { baseParse } from '../src/parser'
|
||||||
import { transform, NodeTransform } from '../src/transform'
|
import { transform, NodeTransform } from '../src/transform'
|
||||||
import {
|
import {
|
||||||
ElementNode,
|
ElementNode,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { baseParse as parse } from '../../src/parse'
|
import { baseParse as parse } from '../../src/parser'
|
||||||
import { transform } from '../../src/transform'
|
import { transform } from '../../src/transform'
|
||||||
import { transformIf } from '../../src/transforms/vIf'
|
import { transformIf } from '../../src/transforms/vIf'
|
||||||
import { transformFor } from '../../src/transforms/vFor'
|
import { transformFor } from '../../src/transforms/vFor'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { baseParse as parse } from '../../src/parse'
|
import { baseParse as parse } from '../../src/parser'
|
||||||
import { transform } from '../../src/transform'
|
import { transform } from '../../src/transform'
|
||||||
import { transformIf } from '../../src/transforms/vIf'
|
import { transformIf } from '../../src/transforms/vIf'
|
||||||
import { transformElement } from '../../src/transforms/transformElement'
|
import { transformElement } from '../../src/transforms/transformElement'
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { TransformContext } from '../src'
|
import { TransformContext } from '../src'
|
||||||
import { Position } from '../src/ast'
|
import { Position } from '../src/ast'
|
||||||
import {
|
import {
|
||||||
getInnerRange,
|
|
||||||
advancePositionWithClone,
|
advancePositionWithClone,
|
||||||
isMemberExpressionNode,
|
isMemberExpressionNode,
|
||||||
isMemberExpressionBrowser,
|
isMemberExpressionBrowser,
|
||||||
|
@ -41,32 +40,6 @@ describe('advancePositionWithClone', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getInnerRange', () => {
|
|
||||||
const loc1 = {
|
|
||||||
source: 'foo\nbar\nbaz',
|
|
||||||
start: p(1, 1, 0),
|
|
||||||
end: p(3, 3, 11)
|
|
||||||
}
|
|
||||||
|
|
||||||
test('at start', () => {
|
|
||||||
const loc2 = getInnerRange(loc1, 0, 4)
|
|
||||||
expect(loc2.start).toEqual(loc1.start)
|
|
||||||
expect(loc2.end.column).toBe(1)
|
|
||||||
expect(loc2.end.line).toBe(2)
|
|
||||||
expect(loc2.end.offset).toBe(4)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('in between', () => {
|
|
||||||
const loc2 = getInnerRange(loc1, 4, 3)
|
|
||||||
expect(loc2.start.column).toBe(1)
|
|
||||||
expect(loc2.start.line).toBe(2)
|
|
||||||
expect(loc2.start.offset).toBe(4)
|
|
||||||
expect(loc2.end.column).toBe(4)
|
|
||||||
expect(loc2.end.line).toBe(2)
|
|
||||||
expect(loc2.end.offset).toBe(7)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('isMemberExpression', () => {
|
describe('isMemberExpression', () => {
|
||||||
function commonAssertions(fn: (str: string) => boolean) {
|
function commonAssertions(fn: (str: string) => boolean) {
|
||||||
// should work
|
// should work
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { isString } from '@vue/shared'
|
import { isString } from '@vue/shared'
|
||||||
import { ForParseResult } from './transforms/vFor'
|
|
||||||
import {
|
import {
|
||||||
RENDER_SLOT,
|
RENDER_SLOT,
|
||||||
CREATE_SLOTS,
|
CREATE_SLOTS,
|
||||||
|
@ -76,7 +75,6 @@ export interface Node {
|
||||||
export interface SourceLocation {
|
export interface SourceLocation {
|
||||||
start: Position
|
start: Position
|
||||||
end: Position
|
end: Position
|
||||||
source: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Position {
|
export interface Position {
|
||||||
|
@ -102,6 +100,7 @@ export type TemplateChildNode =
|
||||||
|
|
||||||
export interface RootNode extends Node {
|
export interface RootNode extends Node {
|
||||||
type: NodeTypes.ROOT
|
type: NodeTypes.ROOT
|
||||||
|
source: string
|
||||||
children: TemplateChildNode[]
|
children: TemplateChildNode[]
|
||||||
helpers: Set<symbol>
|
helpers: Set<symbol>
|
||||||
components: string[]
|
components: string[]
|
||||||
|
@ -182,20 +181,33 @@ export interface CommentNode extends Node {
|
||||||
export interface AttributeNode extends Node {
|
export interface AttributeNode extends Node {
|
||||||
type: NodeTypes.ATTRIBUTE
|
type: NodeTypes.ATTRIBUTE
|
||||||
name: string
|
name: string
|
||||||
|
nameLoc: SourceLocation
|
||||||
value: TextNode | undefined
|
value: TextNode | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DirectiveNode extends Node {
|
export interface DirectiveNode extends Node {
|
||||||
type: NodeTypes.DIRECTIVE
|
type: NodeTypes.DIRECTIVE
|
||||||
|
/**
|
||||||
|
* the normalized name without prefix or shorthands, e.g. "bind", "on"
|
||||||
|
*/
|
||||||
name: string
|
name: string
|
||||||
|
/**
|
||||||
|
* the raw attribute name, preserving shorthand, and including arg & modifiers
|
||||||
|
* this is only used during parse.
|
||||||
|
*/
|
||||||
|
rawName?: string
|
||||||
exp: ExpressionNode | undefined
|
exp: ExpressionNode | undefined
|
||||||
|
/**
|
||||||
|
* the raw expression as a string
|
||||||
|
* only required on directives parsed from templates
|
||||||
|
*/
|
||||||
|
rawExp?: string
|
||||||
arg: ExpressionNode | undefined
|
arg: ExpressionNode | undefined
|
||||||
modifiers: string[]
|
modifiers: string[]
|
||||||
raw?: string
|
|
||||||
/**
|
/**
|
||||||
* optional property to cache the expression parse result for v-for
|
* optional property to cache the expression parse result for v-for
|
||||||
*/
|
*/
|
||||||
parseResult?: ForParseResult
|
forParseResult?: ForParseResult
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,6 +289,14 @@ export interface ForNode extends Node {
|
||||||
codegenNode?: ForCodegenNode
|
codegenNode?: ForCodegenNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ForParseResult {
|
||||||
|
source: ExpressionNode
|
||||||
|
value: ExpressionNode | undefined
|
||||||
|
key: ExpressionNode | undefined
|
||||||
|
index: ExpressionNode | undefined
|
||||||
|
finalized: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface TextCallNode extends Node {
|
export interface TextCallNode extends Node {
|
||||||
type: NodeTypes.TEXT_CALL
|
type: NodeTypes.TEXT_CALL
|
||||||
content: TextNode | InterpolationNode | CompoundExpressionNode
|
content: TextNode | InterpolationNode | CompoundExpressionNode
|
||||||
|
@ -548,17 +568,17 @@ export interface ForIteratorExpression extends FunctionExpression {
|
||||||
// associated with template nodes, so their source locations are just a stub.
|
// associated with template nodes, so their source locations are just a stub.
|
||||||
// Container types like CompoundExpression also don't need a real location.
|
// Container types like CompoundExpression also don't need a real location.
|
||||||
export const locStub: SourceLocation = {
|
export const locStub: SourceLocation = {
|
||||||
source: '',
|
|
||||||
start: { line: 1, column: 1, offset: 0 },
|
start: { line: 1, column: 1, offset: 0 },
|
||||||
end: { line: 1, column: 1, offset: 0 }
|
end: { line: 1, column: 1, offset: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createRoot(
|
export function createRoot(
|
||||||
children: TemplateChildNode[],
|
children: TemplateChildNode[],
|
||||||
loc = locStub
|
source = ''
|
||||||
): RootNode {
|
): RootNode {
|
||||||
return {
|
return {
|
||||||
type: NodeTypes.ROOT,
|
type: NodeTypes.ROOT,
|
||||||
|
source,
|
||||||
children,
|
children,
|
||||||
helpers: new Set(),
|
helpers: new Set(),
|
||||||
components: [],
|
components: [],
|
||||||
|
@ -568,7 +588,7 @@ export function createRoot(
|
||||||
cached: 0,
|
cached: 0,
|
||||||
temps: 0,
|
temps: 0,
|
||||||
codegenNode: undefined,
|
codegenNode: undefined,
|
||||||
loc
|
loc: locStub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ function createCodegenContext(
|
||||||
ssr,
|
ssr,
|
||||||
isTS,
|
isTS,
|
||||||
inSSR,
|
inSSR,
|
||||||
source: ast.loc.source,
|
source: ast.source,
|
||||||
code: ``,
|
code: ``,
|
||||||
column: 1,
|
column: 1,
|
||||||
line: 1,
|
line: 1,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { SourceLocation } from '../ast'
|
import { SourceLocation } from '../ast'
|
||||||
import { CompilerError } from '../errors'
|
import { CompilerError } from '../errors'
|
||||||
|
// @ts-expect-error TODO
|
||||||
import { ParserContext } from '../parse'
|
import { ParserContext } from '../parse'
|
||||||
import { TransformContext } from '../transform'
|
import { TransformContext } from '../transform'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CompilerOptions } from './options'
|
import { CompilerOptions } from './options'
|
||||||
import { baseParse } from './parse'
|
import { baseParse } from './parser/index'
|
||||||
import { transform, NodeTransform, DirectiveTransform } from './transform'
|
import { transform, NodeTransform, DirectiveTransform } from './transform'
|
||||||
import { generate, CodegenResult } from './codegen'
|
import { generate, CodegenResult } from './codegen'
|
||||||
import { RootNode } from './ast'
|
import { RootNode } from './ast'
|
||||||
|
|
|
@ -10,7 +10,7 @@ export {
|
||||||
type BindingMetadata,
|
type BindingMetadata,
|
||||||
BindingTypes
|
BindingTypes
|
||||||
} from './options'
|
} from './options'
|
||||||
export { baseParse, TextModes } from './parse'
|
export { baseParse } from './parser'
|
||||||
export {
|
export {
|
||||||
transform,
|
transform,
|
||||||
type TransformContext,
|
type TransformContext,
|
||||||
|
@ -70,5 +70,3 @@ export {
|
||||||
warnDeprecation,
|
warnDeprecation,
|
||||||
CompilerDeprecationTypes
|
CompilerDeprecationTypes
|
||||||
} from './compat/compatConfig'
|
} from './compat/compatConfig'
|
||||||
|
|
||||||
export { baseParse as newParse } from './parser/index'
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { ElementNode, Namespace, TemplateChildNode, ParentNode } from './ast'
|
import { ElementNode, Namespace, TemplateChildNode, ParentNode } from './ast'
|
||||||
import { TextModes } from './parse'
|
|
||||||
import { CompilerError } from './errors'
|
import { CompilerError } from './errors'
|
||||||
import {
|
import {
|
||||||
NodeTransform,
|
NodeTransform,
|
||||||
|
@ -42,13 +41,6 @@ export interface ParserOptions
|
||||||
* Get tag namespace
|
* Get tag namespace
|
||||||
*/
|
*/
|
||||||
getNamespace?: (tag: string, parent: ElementNode | undefined) => Namespace
|
getNamespace?: (tag: string, parent: ElementNode | undefined) => Namespace
|
||||||
/**
|
|
||||||
* Get text parsing mode for this element
|
|
||||||
*/
|
|
||||||
getTextMode?: (
|
|
||||||
node: ElementNode,
|
|
||||||
parent: ElementNode | undefined
|
|
||||||
) => TextModes
|
|
||||||
/**
|
/**
|
||||||
* @default ['{{', '}}']
|
* @default ['{{', '}}']
|
||||||
*/
|
*/
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,13 +5,15 @@ import {
|
||||||
DirectiveNode,
|
DirectiveNode,
|
||||||
ElementNode,
|
ElementNode,
|
||||||
ElementTypes,
|
ElementTypes,
|
||||||
|
ForParseResult,
|
||||||
Namespaces,
|
Namespaces,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
RootNode,
|
RootNode,
|
||||||
SimpleExpressionNode,
|
SimpleExpressionNode,
|
||||||
SourceLocation,
|
SourceLocation,
|
||||||
TemplateChildNode,
|
TemplateChildNode,
|
||||||
createRoot
|
createRoot,
|
||||||
|
createSimpleExpression
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { ParserOptions } from '../options'
|
import { ParserOptions } from '../options'
|
||||||
import Tokenizer, {
|
import Tokenizer, {
|
||||||
|
@ -24,13 +26,12 @@ import Tokenizer, {
|
||||||
import { CompilerCompatOptions } from '../compat/compatConfig'
|
import { CompilerCompatOptions } from '../compat/compatConfig'
|
||||||
import { NO, extend } from '@vue/shared'
|
import { NO, extend } from '@vue/shared'
|
||||||
import { defaultOnError, defaultOnWarn } from '../errors'
|
import { defaultOnError, defaultOnWarn } from '../errors'
|
||||||
import { isCoreComponent } from '../utils'
|
import { forAliasRE, isCoreComponent } from '../utils'
|
||||||
|
|
||||||
type OptionalOptions =
|
type OptionalOptions =
|
||||||
| 'whitespace'
|
| 'whitespace'
|
||||||
| 'isNativeTag'
|
| 'isNativeTag'
|
||||||
| 'isBuiltInComponent'
|
| 'isBuiltInComponent'
|
||||||
| 'getTextMode'
|
|
||||||
| keyof CompilerCompatOptions
|
| keyof CompilerCompatOptions
|
||||||
|
|
||||||
type MergedParserOptions = Omit<Required<ParserOptions>, OptionalOptions> &
|
type MergedParserOptions = Omit<Required<ParserOptions>, OptionalOptions> &
|
||||||
|
@ -64,7 +65,7 @@ export const defaultParserOptions: MergedParserOptions = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentOptions: MergedParserOptions = defaultParserOptions
|
let currentOptions: MergedParserOptions = defaultParserOptions
|
||||||
let currentRoot: RootNode = createRoot([])
|
let currentRoot: RootNode | null = null
|
||||||
|
|
||||||
// parser state
|
// parser state
|
||||||
let currentInput = ''
|
let currentInput = ''
|
||||||
|
@ -102,14 +103,11 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
}
|
}
|
||||||
addNode({
|
addNode({
|
||||||
type: NodeTypes.INTERPOLATION,
|
type: NodeTypes.INTERPOLATION,
|
||||||
content: {
|
content: createSimpleExpression(
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
getSlice(innerStart, innerEnd),
|
||||||
isStatic: false,
|
false,
|
||||||
// Set `isConstant` to false by default and will decide in transformExpression
|
getLoc(innerStart, innerEnd)
|
||||||
constType: ConstantTypes.NOT_CONSTANT,
|
),
|
||||||
content: getSlice(innerStart, innerEnd),
|
|
||||||
loc: getLoc(innerStart, innerEnd)
|
|
||||||
},
|
|
||||||
loc: getLoc(start, end)
|
loc: getLoc(start, end)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -123,12 +121,7 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
tagType: ElementTypes.ELEMENT, // will be refined on tag close
|
tagType: ElementTypes.ELEMENT, // will be refined on tag close
|
||||||
props: [],
|
props: [],
|
||||||
children: [],
|
children: [],
|
||||||
loc: {
|
loc: getLoc(start - 1),
|
||||||
start: tokenizer.getPos(start - 1),
|
|
||||||
// @ts-expect-error to be attached on tag close
|
|
||||||
end: undefined,
|
|
||||||
source: ''
|
|
||||||
},
|
|
||||||
codegenNode: undefined
|
codegenNode: undefined
|
||||||
}
|
}
|
||||||
currentAttrs.clear()
|
currentAttrs.clear()
|
||||||
|
@ -159,6 +152,7 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
currentProp = {
|
currentProp = {
|
||||||
type: NodeTypes.ATTRIBUTE,
|
type: NodeTypes.ATTRIBUTE,
|
||||||
name: getSlice(start, end),
|
name: getSlice(start, end),
|
||||||
|
nameLoc: getLoc(start, end),
|
||||||
value: undefined,
|
value: undefined,
|
||||||
loc: getLoc(start)
|
loc: getLoc(start)
|
||||||
}
|
}
|
||||||
|
@ -170,6 +164,7 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
currentProp = {
|
currentProp = {
|
||||||
type: NodeTypes.ATTRIBUTE,
|
type: NodeTypes.ATTRIBUTE,
|
||||||
name: raw,
|
name: raw,
|
||||||
|
nameLoc: getLoc(start, end),
|
||||||
value: undefined,
|
value: undefined,
|
||||||
loc: getLoc(start)
|
loc: getLoc(start)
|
||||||
}
|
}
|
||||||
|
@ -185,7 +180,7 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
currentProp = {
|
currentProp = {
|
||||||
type: NodeTypes.DIRECTIVE,
|
type: NodeTypes.DIRECTIVE,
|
||||||
name,
|
name,
|
||||||
raw,
|
rawName: raw,
|
||||||
exp: undefined,
|
exp: undefined,
|
||||||
arg: undefined,
|
arg: undefined,
|
||||||
modifiers: [],
|
modifiers: [],
|
||||||
|
@ -209,17 +204,15 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
const arg = getSlice(start, end)
|
const arg = getSlice(start, end)
|
||||||
if (inVPre) {
|
if (inVPre) {
|
||||||
;(currentProp as AttributeNode).name += arg
|
;(currentProp as AttributeNode).name += arg
|
||||||
|
;(currentProp as AttributeNode).nameLoc.end = tokenizer.getPos(end)
|
||||||
} else {
|
} else {
|
||||||
const isStatic = arg[0] !== `[`
|
const isStatic = arg[0] !== `[`
|
||||||
;(currentProp as DirectiveNode).arg = {
|
;(currentProp as DirectiveNode).arg = createSimpleExpression(
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
arg,
|
||||||
content: arg,
|
|
||||||
isStatic,
|
isStatic,
|
||||||
constType: isStatic
|
getLoc(start, end),
|
||||||
? ConstantTypes.CAN_STRINGIFY
|
isStatic ? ConstantTypes.CAN_STRINGIFY : ConstantTypes.NOT_CONSTANT
|
||||||
: ConstantTypes.NOT_CONSTANT,
|
)
|
||||||
loc: getLoc(start, end)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -227,6 +220,7 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
const mod = getSlice(start, end)
|
const mod = getSlice(start, end)
|
||||||
if (inVPre) {
|
if (inVPre) {
|
||||||
;(currentProp as AttributeNode).name += '.' + mod
|
;(currentProp as AttributeNode).name += '.' + mod
|
||||||
|
;(currentProp as AttributeNode).nameLoc.end = tokenizer.getPos(end)
|
||||||
} else {
|
} else {
|
||||||
;(currentProp as DirectiveNode).modifiers.push(mod)
|
;(currentProp as DirectiveNode).modifiers.push(mod)
|
||||||
}
|
}
|
||||||
|
@ -247,7 +241,7 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
const start = currentProp!.loc.start.offset
|
const start = currentProp!.loc.start.offset
|
||||||
const name = getSlice(start, end)
|
const name = getSlice(start, end)
|
||||||
if (currentProp!.type === NodeTypes.DIRECTIVE) {
|
if (currentProp!.type === NodeTypes.DIRECTIVE) {
|
||||||
currentProp!.raw = name
|
currentProp!.rawName = name
|
||||||
}
|
}
|
||||||
if (currentAttrs.has(name)) {
|
if (currentAttrs.has(name)) {
|
||||||
currentProp = null
|
currentProp = null
|
||||||
|
@ -273,15 +267,14 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// directive
|
// directive
|
||||||
currentProp.exp = {
|
currentProp.rawExp = currentAttrValue
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
currentProp.exp = createSimpleExpression(
|
||||||
content: currentAttrValue,
|
currentAttrValue,
|
||||||
isStatic: false,
|
false,
|
||||||
// Treat as non-constant by default. This can be potentially set
|
getLoc(currentAttrStartIndex, currentAttrEndIndex)
|
||||||
// to other values by `transformExpression` to make it eligible
|
)
|
||||||
// for hoisting.
|
if (currentProp.name === 'for') {
|
||||||
constType: ConstantTypes.NOT_CONSTANT,
|
currentProp.forParseResult = parseForExpression(currentProp.exp)
|
||||||
loc: getLoc(currentAttrStartIndex, currentAttrEndIndex)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,6 +312,73 @@ const tokenizer = new Tokenizer(stack, {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// This regex doesn't cover the case if key or index aliases have destructuring,
|
||||||
|
// but those do not make sense in the first place, so this works in practice.
|
||||||
|
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
||||||
|
const stripParensRE = /^\(|\)$/g
|
||||||
|
|
||||||
|
function parseForExpression(
|
||||||
|
input: SimpleExpressionNode
|
||||||
|
): ForParseResult | undefined {
|
||||||
|
const loc = input.loc
|
||||||
|
const exp = input.content
|
||||||
|
const inMatch = exp.match(forAliasRE)
|
||||||
|
if (!inMatch) return
|
||||||
|
|
||||||
|
const [, LHS, RHS] = inMatch
|
||||||
|
|
||||||
|
const createAliasExpression = (content: string, offset: number) => {
|
||||||
|
const start = loc.start.offset + offset
|
||||||
|
const end = start + content.length
|
||||||
|
return createSimpleExpression(content, false, getLoc(start, end))
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: ForParseResult = {
|
||||||
|
source: createAliasExpression(RHS.trim(), exp.indexOf(RHS, LHS.length)),
|
||||||
|
value: undefined,
|
||||||
|
key: undefined,
|
||||||
|
index: undefined,
|
||||||
|
finalized: false
|
||||||
|
}
|
||||||
|
|
||||||
|
let valueContent = LHS.trim().replace(stripParensRE, '').trim()
|
||||||
|
const trimmedOffset = LHS.indexOf(valueContent)
|
||||||
|
|
||||||
|
const iteratorMatch = valueContent.match(forIteratorRE)
|
||||||
|
if (iteratorMatch) {
|
||||||
|
valueContent = valueContent.replace(forIteratorRE, '').trim()
|
||||||
|
|
||||||
|
const keyContent = iteratorMatch[1].trim()
|
||||||
|
let keyOffset: number | undefined
|
||||||
|
if (keyContent) {
|
||||||
|
keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length)
|
||||||
|
result.key = createAliasExpression(keyContent, keyOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iteratorMatch[2]) {
|
||||||
|
const indexContent = iteratorMatch[2].trim()
|
||||||
|
|
||||||
|
if (indexContent) {
|
||||||
|
result.index = createAliasExpression(
|
||||||
|
indexContent,
|
||||||
|
exp.indexOf(
|
||||||
|
indexContent,
|
||||||
|
result.key
|
||||||
|
? keyOffset! + keyContent.length
|
||||||
|
: trimmedOffset + valueContent.length
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueContent) {
|
||||||
|
result.value = createAliasExpression(valueContent, trimmedOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
function getSlice(start: number, end: number) {
|
function getSlice(start: number, end: number) {
|
||||||
return currentInput.slice(start, end)
|
return currentInput.slice(start, end)
|
||||||
}
|
}
|
||||||
|
@ -356,11 +416,7 @@ function onText(content: string, start: number, end: number) {
|
||||||
parent.children.push({
|
parent.children.push({
|
||||||
type: NodeTypes.TEXT,
|
type: NodeTypes.TEXT,
|
||||||
content,
|
content,
|
||||||
loc: {
|
loc: getLoc(start, end)
|
||||||
start: tokenizer.getPos(start),
|
|
||||||
end: tokenizer.getPos(end),
|
|
||||||
source: ''
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,7 +459,7 @@ function isFragmentTemplate({ tag, props }: ElementNode): boolean {
|
||||||
for (let i = 0; i < props.length; i++) {
|
for (let i = 0; i < props.length; i++) {
|
||||||
if (
|
if (
|
||||||
props[i].type === NodeTypes.DIRECTIVE &&
|
props[i].type === NodeTypes.DIRECTIVE &&
|
||||||
specialTemplateDir.has(props[i].name)
|
specialTemplateDir.has((props[i] as DirectiveNode).name)
|
||||||
) {
|
) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -571,7 +627,11 @@ function getLoc(start: number, end?: number): SourceLocation {
|
||||||
function dirToAttr(dir: DirectiveNode): AttributeNode {
|
function dirToAttr(dir: DirectiveNode): AttributeNode {
|
||||||
const attr: AttributeNode = {
|
const attr: AttributeNode = {
|
||||||
type: NodeTypes.ATTRIBUTE,
|
type: NodeTypes.ATTRIBUTE,
|
||||||
name: dir.raw!,
|
name: dir.rawName!,
|
||||||
|
nameLoc: getLoc(
|
||||||
|
dir.loc.start.offset,
|
||||||
|
dir.loc.start.offset + dir.rawName!.length
|
||||||
|
),
|
||||||
value: undefined,
|
value: undefined,
|
||||||
loc: dir.loc
|
loc: dir.loc
|
||||||
}
|
}
|
||||||
|
@ -622,9 +682,9 @@ export function baseParse(input: string, options?: ParserOptions): RootNode {
|
||||||
tokenizer.delimiterClose = toCharCodes(delimiters[1])
|
tokenizer.delimiterClose = toCharCodes(delimiters[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = (currentRoot = createRoot([]))
|
const root = (currentRoot = createRoot([], input))
|
||||||
tokenizer.parse(currentInput)
|
tokenizer.parse(currentInput)
|
||||||
root.loc.end = tokenizer.getPos(input.length)
|
|
||||||
root.children = condenseWhitespace(root.children)
|
root.children = condenseWhitespace(root.children)
|
||||||
|
currentRoot = null
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@ import {
|
||||||
GUARD_REACTIVE_PROPS
|
GUARD_REACTIVE_PROPS
|
||||||
} from '../runtimeHelpers'
|
} from '../runtimeHelpers'
|
||||||
import {
|
import {
|
||||||
getInnerRange,
|
|
||||||
toValidAssetId,
|
toValidAssetId,
|
||||||
findProp,
|
findProp,
|
||||||
isCoreComponent,
|
isCoreComponent,
|
||||||
|
@ -160,8 +159,7 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||||
context.onError(
|
context.onError(
|
||||||
createCompilerError(ErrorCodes.X_KEEP_ALIVE_INVALID_CHILDREN, {
|
createCompilerError(ErrorCodes.X_KEEP_ALIVE_INVALID_CHILDREN, {
|
||||||
start: node.children[0].loc.start,
|
start: node.children[0].loc.start,
|
||||||
end: node.children[node.children.length - 1].loc.end,
|
end: node.children[node.children.length - 1].loc.end
|
||||||
source: ''
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -489,7 +487,7 @@ export function buildProps(
|
||||||
// static attribute
|
// static attribute
|
||||||
const prop = props[i]
|
const prop = props[i]
|
||||||
if (prop.type === NodeTypes.ATTRIBUTE) {
|
if (prop.type === NodeTypes.ATTRIBUTE) {
|
||||||
const { loc, name, value } = prop
|
const { loc, name, nameLoc, value } = prop
|
||||||
let isStatic = true
|
let isStatic = true
|
||||||
if (name === 'ref') {
|
if (name === 'ref') {
|
||||||
hasRef = true
|
hasRef = true
|
||||||
|
@ -536,11 +534,7 @@ export function buildProps(
|
||||||
}
|
}
|
||||||
properties.push(
|
properties.push(
|
||||||
createObjectProperty(
|
createObjectProperty(
|
||||||
createSimpleExpression(
|
createSimpleExpression(name, true, nameLoc),
|
||||||
name,
|
|
||||||
true,
|
|
||||||
getInnerRange(loc, 0, name.length)
|
|
||||||
),
|
|
||||||
createSimpleExpression(
|
createSimpleExpression(
|
||||||
value ? value.content : '',
|
value ? value.content : '',
|
||||||
isStatic,
|
isStatic,
|
||||||
|
|
|
@ -336,7 +336,6 @@ export function processExpression(
|
||||||
id.name,
|
id.name,
|
||||||
false,
|
false,
|
||||||
{
|
{
|
||||||
source,
|
|
||||||
start: advancePositionWithClone(node.loc.start, source, start),
|
start: advancePositionWithClone(node.loc.start, source, start),
|
||||||
end: advancePositionWithClone(node.loc.start, source, end)
|
end: advancePositionWithClone(node.loc.start, source, end)
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,6 @@ import {
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
ExpressionNode,
|
ExpressionNode,
|
||||||
createSimpleExpression,
|
createSimpleExpression,
|
||||||
SourceLocation,
|
|
||||||
SimpleExpressionNode,
|
SimpleExpressionNode,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
createFunctionExpression,
|
createFunctionExpression,
|
||||||
|
@ -28,17 +27,16 @@ import {
|
||||||
createBlockStatement,
|
createBlockStatement,
|
||||||
createCompoundExpression,
|
createCompoundExpression,
|
||||||
getVNodeBlockHelper,
|
getVNodeBlockHelper,
|
||||||
getVNodeHelper
|
getVNodeHelper,
|
||||||
|
ForParseResult
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import {
|
import {
|
||||||
getInnerRange,
|
|
||||||
findProp,
|
findProp,
|
||||||
isTemplateNode,
|
isTemplateNode,
|
||||||
isSlotOutlet,
|
isSlotOutlet,
|
||||||
injectProp,
|
injectProp,
|
||||||
findDir,
|
findDir
|
||||||
forAliasRE
|
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import {
|
import {
|
||||||
RENDER_LIST,
|
RENDER_LIST,
|
||||||
|
@ -256,12 +254,7 @@ export function processFor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseResult = parseForExpression(
|
const parseResult = dir.forParseResult
|
||||||
// can only be simple expression because vFor transform is applied
|
|
||||||
// before expression transform.
|
|
||||||
dir.exp as SimpleExpressionNode,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!parseResult) {
|
if (!parseResult) {
|
||||||
context.onError(
|
context.onError(
|
||||||
|
@ -270,6 +263,8 @@ export function processFor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalizeForParseResult(parseResult, context)
|
||||||
|
|
||||||
const { addIdentifiers, removeIdentifiers, scopes } = context
|
const { addIdentifiers, removeIdentifiers, scopes } = context
|
||||||
const { source, value, key, index } = parseResult
|
const { source, value, key, index } = parseResult
|
||||||
|
|
||||||
|
@ -309,128 +304,50 @@ export function processFor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This regex doesn't cover the case if key or index aliases have destructuring,
|
export function finalizeForParseResult(
|
||||||
// but those do not make sense in the first place, so this works in practice.
|
result: ForParseResult,
|
||||||
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
|
||||||
const stripParensRE = /^\(|\)$/g
|
|
||||||
|
|
||||||
export interface ForParseResult {
|
|
||||||
source: ExpressionNode
|
|
||||||
value: ExpressionNode | undefined
|
|
||||||
key: ExpressionNode | undefined
|
|
||||||
index: ExpressionNode | undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseForExpression(
|
|
||||||
input: SimpleExpressionNode,
|
|
||||||
context: TransformContext
|
context: TransformContext
|
||||||
): ForParseResult | undefined {
|
) {
|
||||||
const loc = input.loc
|
if (result.finalized) return
|
||||||
const exp = input.content
|
|
||||||
const inMatch = exp.match(forAliasRE)
|
|
||||||
if (!inMatch) return
|
|
||||||
|
|
||||||
const [, LHS, RHS] = inMatch
|
|
||||||
|
|
||||||
const result: ForParseResult = {
|
|
||||||
source: createAliasExpression(
|
|
||||||
loc,
|
|
||||||
RHS.trim(),
|
|
||||||
exp.indexOf(RHS, LHS.length)
|
|
||||||
),
|
|
||||||
value: undefined,
|
|
||||||
key: undefined,
|
|
||||||
index: undefined
|
|
||||||
}
|
|
||||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
if (!__BROWSER__ && context.prefixIdentifiers) {
|
||||||
result.source = processExpression(
|
result.source = processExpression(
|
||||||
result.source as SimpleExpressionNode,
|
result.source as SimpleExpressionNode,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
}
|
if (result.key) {
|
||||||
if (__DEV__ && __BROWSER__) {
|
result.key = processExpression(
|
||||||
validateBrowserExpression(result.source as SimpleExpressionNode, context)
|
result.key as SimpleExpressionNode,
|
||||||
}
|
context,
|
||||||
|
true
|
||||||
let valueContent = LHS.trim().replace(stripParensRE, '').trim()
|
)
|
||||||
const trimmedOffset = LHS.indexOf(valueContent)
|
|
||||||
|
|
||||||
const iteratorMatch = valueContent.match(forIteratorRE)
|
|
||||||
if (iteratorMatch) {
|
|
||||||
valueContent = valueContent.replace(forIteratorRE, '').trim()
|
|
||||||
|
|
||||||
const keyContent = iteratorMatch[1].trim()
|
|
||||||
let keyOffset: number | undefined
|
|
||||||
if (keyContent) {
|
|
||||||
keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length)
|
|
||||||
result.key = createAliasExpression(loc, keyContent, keyOffset)
|
|
||||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
|
||||||
result.key = processExpression(result.key, context, true)
|
|
||||||
}
|
|
||||||
if (__DEV__ && __BROWSER__) {
|
|
||||||
validateBrowserExpression(
|
|
||||||
result.key as SimpleExpressionNode,
|
|
||||||
context,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (result.index) {
|
||||||
if (iteratorMatch[2]) {
|
result.index = processExpression(
|
||||||
const indexContent = iteratorMatch[2].trim()
|
result.index as SimpleExpressionNode,
|
||||||
|
|
||||||
if (indexContent) {
|
|
||||||
result.index = createAliasExpression(
|
|
||||||
loc,
|
|
||||||
indexContent,
|
|
||||||
exp.indexOf(
|
|
||||||
indexContent,
|
|
||||||
result.key
|
|
||||||
? keyOffset! + keyContent.length
|
|
||||||
: trimmedOffset + valueContent.length
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
|
||||||
result.index = processExpression(result.index, context, true)
|
|
||||||
}
|
|
||||||
if (__DEV__ && __BROWSER__) {
|
|
||||||
validateBrowserExpression(
|
|
||||||
result.index as SimpleExpressionNode,
|
|
||||||
context,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valueContent) {
|
|
||||||
result.value = createAliasExpression(loc, valueContent, trimmedOffset)
|
|
||||||
if (!__BROWSER__ && context.prefixIdentifiers) {
|
|
||||||
result.value = processExpression(result.value, context, true)
|
|
||||||
}
|
|
||||||
if (__DEV__ && __BROWSER__) {
|
|
||||||
validateBrowserExpression(
|
|
||||||
result.value as SimpleExpressionNode,
|
|
||||||
context,
|
context,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (__DEV__ && __BROWSER__) {
|
||||||
return result
|
validateBrowserExpression(result.source as SimpleExpressionNode, context)
|
||||||
}
|
if (result.key) {
|
||||||
|
validateBrowserExpression(
|
||||||
function createAliasExpression(
|
result.key as SimpleExpressionNode,
|
||||||
range: SourceLocation,
|
context,
|
||||||
content: string,
|
true
|
||||||
offset: number
|
)
|
||||||
): SimpleExpressionNode {
|
}
|
||||||
return createSimpleExpression(
|
if (result.index) {
|
||||||
content,
|
validateBrowserExpression(
|
||||||
false,
|
result.index as SimpleExpressionNode,
|
||||||
getInnerRange(range, offset, content.length)
|
context,
|
||||||
)
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.finalized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createForLoopParams(
|
export function createForLoopParams(
|
||||||
|
|
|
@ -29,7 +29,9 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
|
||||||
return createTransformProps()
|
return createTransformProps()
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawExp = exp.loc.source
|
// we assume v-model directives are always parsed
|
||||||
|
// (not artificially created by a transform)
|
||||||
|
const rawExp = dir.rawExp!
|
||||||
const expString =
|
const expString =
|
||||||
exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : rawExp
|
exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : rawExp
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ import {
|
||||||
SourceLocation,
|
SourceLocation,
|
||||||
createConditionalExpression,
|
createConditionalExpression,
|
||||||
ConditionalExpression,
|
ConditionalExpression,
|
||||||
SimpleExpressionNode,
|
|
||||||
FunctionExpression,
|
FunctionExpression,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
|
@ -32,7 +31,7 @@ import {
|
||||||
isStaticExp
|
isStaticExp
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { CREATE_SLOTS, RENDER_LIST, WITH_CTX } from '../runtimeHelpers'
|
import { CREATE_SLOTS, RENDER_LIST, WITH_CTX } from '../runtimeHelpers'
|
||||||
import { parseForExpression, createForLoopParams } from './vFor'
|
import { createForLoopParams, finalizeForParseResult } from './vFor'
|
||||||
import { SlotFlags, slotFlagsText } from '@vue/shared'
|
import { SlotFlags, slotFlagsText } from '@vue/shared'
|
||||||
|
|
||||||
const defaultFallback = createSimpleExpression(`undefined`, false)
|
const defaultFallback = createSimpleExpression(`undefined`, false)
|
||||||
|
@ -78,11 +77,9 @@ export const trackVForSlotScopes: NodeTransform = (node, context) => {
|
||||||
node.props.some(isVSlot) &&
|
node.props.some(isVSlot) &&
|
||||||
(vFor = findDir(node, 'for'))
|
(vFor = findDir(node, 'for'))
|
||||||
) {
|
) {
|
||||||
const result = (vFor.parseResult = parseForExpression(
|
const result = vFor.forParseResult
|
||||||
vFor.exp as SimpleExpressionNode,
|
|
||||||
context
|
|
||||||
))
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
finalizeForParseResult(result, context)
|
||||||
const { value, key, index } = result
|
const { value, key, index } = result
|
||||||
const { addIdentifiers, removeIdentifiers } = context
|
const { addIdentifiers, removeIdentifiers } = context
|
||||||
value && addIdentifiers(value)
|
value && addIdentifiers(value)
|
||||||
|
@ -266,10 +263,9 @@ export function buildSlots(
|
||||||
}
|
}
|
||||||
} else if (vFor) {
|
} else if (vFor) {
|
||||||
hasDynamicSlots = true
|
hasDynamicSlots = true
|
||||||
const parseResult =
|
const parseResult = vFor.forParseResult
|
||||||
vFor.parseResult ||
|
|
||||||
parseForExpression(vFor.exp as SimpleExpressionNode, context)
|
|
||||||
if (parseResult) {
|
if (parseResult) {
|
||||||
|
finalizeForParseResult(parseResult, context)
|
||||||
// Render the dynamic slots as an array and add it to the createSlot()
|
// Render the dynamic slots as an array and add it to the createSlot()
|
||||||
// args. The runtime knows how to handle it appropriately.
|
// args. The runtime knows how to handle it appropriately.
|
||||||
dynamicSlots.push(
|
dynamicSlots.push(
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
SourceLocation,
|
|
||||||
Position,
|
Position,
|
||||||
ElementNode,
|
ElementNode,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
|
@ -176,31 +175,6 @@ export const isMemberExpression = __BROWSER__
|
||||||
? isMemberExpressionBrowser
|
? isMemberExpressionBrowser
|
||||||
: isMemberExpressionNode
|
: isMemberExpressionNode
|
||||||
|
|
||||||
export function getInnerRange(
|
|
||||||
loc: SourceLocation,
|
|
||||||
offset: number,
|
|
||||||
length: number
|
|
||||||
): SourceLocation {
|
|
||||||
__TEST__ && assert(offset <= loc.source.length)
|
|
||||||
const source = loc.source.slice(offset, offset + length)
|
|
||||||
const newLoc: SourceLocation = {
|
|
||||||
source,
|
|
||||||
start: advancePositionWithClone(loc.start, loc.source, offset),
|
|
||||||
end: loc.end
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length != null) {
|
|
||||||
__TEST__ && assert(offset + length <= loc.source.length)
|
|
||||||
newLoc.end = advancePositionWithClone(
|
|
||||||
loc.start,
|
|
||||||
loc.source,
|
|
||||||
offset + length
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newLoc
|
|
||||||
}
|
|
||||||
|
|
||||||
export function advancePositionWithClone(
|
export function advancePositionWithClone(
|
||||||
pos: Position,
|
pos: Position,
|
||||||
source: string,
|
source: string,
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
import {
|
import { ParserOptions, ElementNode, NodeTypes } from '@vue/compiler-core'
|
||||||
TextModes,
|
|
||||||
ParserOptions,
|
|
||||||
ElementNode,
|
|
||||||
NodeTypes
|
|
||||||
} from '@vue/compiler-core'
|
|
||||||
import { isVoidTag, isHTMLTag, isSVGTag } from '@vue/shared'
|
import { isVoidTag, isHTMLTag, isSVGTag } from '@vue/shared'
|
||||||
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
|
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
|
||||||
import { decodeHtml } from './decodeHtml'
|
import { decodeHtml } from './decodeHtml'
|
||||||
|
@ -16,6 +11,7 @@ export const enum DOMNamespaces {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parserOptions: ParserOptions = {
|
export const parserOptions: ParserOptions = {
|
||||||
|
parseMode: 'html',
|
||||||
isVoidTag,
|
isVoidTag,
|
||||||
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag),
|
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag),
|
||||||
isPreTag: tag => tag === 'pre',
|
isPreTag: tag => tag === 'pre',
|
||||||
|
@ -75,18 +71,5 @@ export const parserOptions: ParserOptions = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ns
|
return ns
|
||||||
},
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-fragments
|
|
||||||
getTextMode({ tag, ns }: ElementNode): TextModes {
|
|
||||||
if (ns === DOMNamespaces.HTML) {
|
|
||||||
if (tag === 'textarea' || tag === 'title') {
|
|
||||||
return TextModes.RCDATA
|
|
||||||
}
|
|
||||||
if (tag === 'style' || tag === 'script') {
|
|
||||||
return TextModes.RAWTEXT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TextModes.DATA
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,7 @@ export const transformTransition: NodeTransform = (node, context) => {
|
||||||
DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN,
|
DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN,
|
||||||
{
|
{
|
||||||
start: node.children[0].loc.start,
|
start: node.children[0].loc.start,
|
||||||
end: node.children[node.children.length - 1].loc.end,
|
end: node.children[node.children.length - 1].loc.end
|
||||||
source: ''
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -43,6 +42,7 @@ export const transformTransition: NodeTransform = (node, context) => {
|
||||||
node.props.push({
|
node.props.push({
|
||||||
type: NodeTypes.ATTRIBUTE,
|
type: NodeTypes.ATTRIBUTE,
|
||||||
name: 'persisted',
|
name: 'persisted',
|
||||||
|
nameLoc: node.loc,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
loc: node.loc
|
loc: node.loc
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {
|
||||||
ElementNode,
|
ElementNode,
|
||||||
SourceLocation,
|
SourceLocation,
|
||||||
CompilerError,
|
CompilerError,
|
||||||
TextModes,
|
|
||||||
BindingMetadata
|
BindingMetadata
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import * as CompilerDOM from '@vue/compiler-dom'
|
import * as CompilerDOM from '@vue/compiler-dom'
|
||||||
|
@ -128,31 +127,7 @@ export function parse(
|
||||||
|
|
||||||
const errors: (CompilerError | SyntaxError)[] = []
|
const errors: (CompilerError | SyntaxError)[] = []
|
||||||
const ast = compiler.parse(source, {
|
const ast = compiler.parse(source, {
|
||||||
// there are no components at SFC parsing level
|
parseMode: 'sfc',
|
||||||
isNativeTag: () => true,
|
|
||||||
// preserve all whitespaces
|
|
||||||
isPreTag: () => true,
|
|
||||||
getTextMode: ({ tag, props }, parent) => {
|
|
||||||
// all top level elements except <template> are parsed as raw text
|
|
||||||
// containers
|
|
||||||
if (
|
|
||||||
(!parent && tag !== 'template') ||
|
|
||||||
// <template lang="xxx"> should also be treated as raw text
|
|
||||||
(tag === 'template' &&
|
|
||||||
props.some(
|
|
||||||
p =>
|
|
||||||
p.type === NodeTypes.ATTRIBUTE &&
|
|
||||||
p.name === 'lang' &&
|
|
||||||
p.value &&
|
|
||||||
p.value.content &&
|
|
||||||
p.value.content !== 'html'
|
|
||||||
))
|
|
||||||
) {
|
|
||||||
return TextModes.RAWTEXT
|
|
||||||
} else {
|
|
||||||
return TextModes.DATA
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: e => {
|
onError: e => {
|
||||||
errors.push(e)
|
errors.push(e)
|
||||||
}
|
}
|
||||||
|
@ -188,7 +163,9 @@ export function parse(
|
||||||
`difference from stateful ones. Just use a normal <template> ` +
|
`difference from stateful ones. Just use a normal <template> ` +
|
||||||
`instead.`
|
`instead.`
|
||||||
) as CompilerError
|
) as CompilerError
|
||||||
err.loc = node.props.find(p => p.name === 'functional')!.loc
|
err.loc = node.props.find(
|
||||||
|
p => p.type === NodeTypes.ATTRIBUTE && p.name === 'functional'
|
||||||
|
)!.loc
|
||||||
errors.push(err)
|
errors.push(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -314,7 +291,7 @@ function createBlock(
|
||||||
end = node.children[node.children.length - 1].loc.end
|
end = node.children[node.children.length - 1].loc.end
|
||||||
content = source.slice(start.offset, end.offset)
|
content = source.slice(start.offset, end.offset)
|
||||||
} else {
|
} else {
|
||||||
const offset = node.loc.source.indexOf(`</`)
|
const offset = source.indexOf(`</`, start.offset)
|
||||||
if (offset > -1) {
|
if (offset > -1) {
|
||||||
start = {
|
start = {
|
||||||
line: start.line,
|
line: start.line,
|
||||||
|
@ -341,18 +318,19 @@ function createBlock(
|
||||||
}
|
}
|
||||||
node.props.forEach(p => {
|
node.props.forEach(p => {
|
||||||
if (p.type === NodeTypes.ATTRIBUTE) {
|
if (p.type === NodeTypes.ATTRIBUTE) {
|
||||||
attrs[p.name] = p.value ? p.value.content || true : true
|
const name = p.name
|
||||||
if (p.name === 'lang') {
|
attrs[name] = p.value ? p.value.content || true : true
|
||||||
|
if (name === 'lang') {
|
||||||
block.lang = p.value && p.value.content
|
block.lang = p.value && p.value.content
|
||||||
} else if (p.name === 'src') {
|
} else if (name === 'src') {
|
||||||
block.src = p.value && p.value.content
|
block.src = p.value && p.value.content
|
||||||
} else if (type === 'style') {
|
} else if (type === 'style') {
|
||||||
if (p.name === 'scoped') {
|
if (name === 'scoped') {
|
||||||
;(block as SFCStyleBlock).scoped = true
|
;(block as SFCStyleBlock).scoped = true
|
||||||
} else if (p.name === 'module') {
|
} else if (name === 'module') {
|
||||||
;(block as SFCStyleBlock).module = attrs[p.name]
|
;(block as SFCStyleBlock).module = attrs[name]
|
||||||
}
|
}
|
||||||
} else if (type === 'script' && p.name === 'setup') {
|
} else if (type === 'script' && name === 'setup') {
|
||||||
;(block as SFCScriptBlock).setup = attrs.setup
|
;(block as SFCScriptBlock).setup = attrs.setup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,14 +292,15 @@ export const ssrTransformElement: NodeTransform = (node, context) => {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// special case: value on <textarea>
|
// special case: value on <textarea>
|
||||||
if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
|
const name = prop.name
|
||||||
|
if (node.tag === 'textarea' && name === 'value' && prop.value) {
|
||||||
rawChildrenMap.set(node, escapeHtml(prop.value.content))
|
rawChildrenMap.set(node, escapeHtml(prop.value.content))
|
||||||
} else if (!needMergeProps) {
|
} else if (!needMergeProps) {
|
||||||
if (prop.name === 'key' || prop.name === 'ref') {
|
if (name === 'key' || name === 'ref') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// static prop
|
// static prop
|
||||||
if (prop.name === 'class' && prop.value) {
|
if (name === 'class' && prop.value) {
|
||||||
staticClassBinding = JSON.stringify(prop.value.content)
|
staticClassBinding = JSON.stringify(prop.value.content)
|
||||||
}
|
}
|
||||||
openTag.push(
|
openTag.push(
|
||||||
|
|
|
@ -91,5 +91,3 @@ registerRuntimeCompiler(compileToFunction)
|
||||||
|
|
||||||
export { compileToFunction as compile }
|
export { compileToFunction as compile }
|
||||||
export * from '@vue/runtime-dom'
|
export * from '@vue/runtime-dom'
|
||||||
|
|
||||||
export { newParse } from '@vue/compiler-dom'
|
|
||||||
|
|
Loading…
Reference in New Issue