1import deindent from 'de-indent'2import { parseHTML } from 'compiler/parser/html-parser'3import { makeMap } from 'shared/util'4import { ASTAttr, WarningMessage } from 'types/compiler'5import { BindingMetadata, RawSourceMap } from './types'6import type { ImportBinding } from './compileScript'78export const DEFAULT_FILENAME = 'anonymous.vue'910const splitRE = /\r?\n/g11const replaceRE = /./g12const isSpecialTag = makeMap('script,style,template', true)1314export interface SFCCustomBlock {15 type: string16 content: string17 attrs: { [key: string]: string | true }18 start: number19 end: number20 src?: string21 map?: RawSourceMap22}2324export interface SFCBlock extends SFCCustomBlock {25 lang?: string26 scoped?: boolean27 module?: string | boolean28}2930export interface SFCScriptBlock extends SFCBlock {31 type: 'script'32 setup?: string | boolean33 bindings?: BindingMetadata34 imports?: Record<string, ImportBinding>35 /**36 * import('\@babel/types').Statement37 */38 scriptAst?: any[]39 /**40 * import('\@babel/types').Statement41 */42 scriptSetupAst?: any[]43}4445export interface SFCDescriptor {46 source: string47 filename: string48 template: SFCBlock | null49 script: SFCScriptBlock | null50 scriptSetup: SFCScriptBlock | null51 styles: SFCBlock[]52 customBlocks: SFCCustomBlock[]53 cssVars: string[]5455 errors: (string | WarningMessage)[]5657 /**58 * compare with an existing descriptor to determine whether HMR should perform59 * a reload vs. re-render.60 *61 * Note: this comparison assumes the prev/next script are already identical,62 * and only checks the special case where `<script setup lang="ts">` unused63 * import pruning result changes due to template changes.64 */65 shouldForceReload: (prevImports: Record<string, ImportBinding>) => boolean66}6768export interface VueTemplateCompilerParseOptions {69 pad?: 'line' | 'space' | boolean70 deindent?: boolean71 outputSourceRange?: boolean72}7374/**75 * Parse a single-file component (*.vue) file into an SFC Descriptor Object.76 */77export function parseComponent(78 source: string,79 options: VueTemplateCompilerParseOptions = {}80): SFCDescriptor {81 const sfc: SFCDescriptor = {82 source,83 filename: DEFAULT_FILENAME,84 template: null,85 script: null,86 scriptSetup: null, // TODO87 styles: [],88 customBlocks: [],89 cssVars: [],90 errors: [],91 shouldForceReload: null as any // attached in parse() by compiler-sfc92 }93 let depth = 094 let currentBlock: SFCBlock | null = null9596 let warn: any = msg => {97 sfc.errors.push(msg)98 }99100 if (__DEV__ && options.outputSourceRange) {101 warn = (msg, range) => {102 const data: WarningMessage = { msg }103 if (range.start != null) {104 data.start = range.start105 }106 if (range.end != null) {107 data.end = range.end108 }109 sfc.errors.push(data)110 }111 }112113 function start(114 tag: string,115 attrs: ASTAttr[],116 unary: boolean,117 start: number,118 end: number119 ) {120 if (depth === 0) {121 currentBlock = {122 type: tag,123 content: '',124 start: end,125 end: 0, // will be set on tag close126 attrs: attrs.reduce((cumulated, { name, value }) => {127 cumulated[name] = value || true128 return cumulated129 }, {})130 }131132 if (typeof currentBlock.attrs.src === 'string') {133 currentBlock.src = currentBlock.attrs.src134 }135136 if (isSpecialTag(tag)) {137 checkAttrs(currentBlock, attrs)138 if (tag === 'script') {139 const block = currentBlock as SFCScriptBlock140 if (block.attrs.setup) {141 block.setup = currentBlock.attrs.setup142 sfc.scriptSetup = block143 } else {144 sfc.script = block145 }146 } else if (tag === 'style') {147 sfc.styles.push(currentBlock)148 } else {149 sfc[tag] = currentBlock150 }151 } else {152 // custom blocks153 sfc.customBlocks.push(currentBlock)154 }155 }156 if (!unary) {157 depth++158 }159 }160161 function checkAttrs(block: SFCBlock, attrs: ASTAttr[]) {162 for (let i = 0; i < attrs.length; i++) {163 const attr = attrs[i]164 if (attr.name === 'lang') {165 block.lang = attr.value166 }167 if (attr.name === 'scoped') {168 block.scoped = true169 }170 if (attr.name === 'module') {171 block.module = attr.value || true172 }173 }174 }175176 function end(tag: string, start: number) {177 if (depth === 1 && currentBlock) {178 currentBlock.end = start179 let text = source.slice(currentBlock.start, currentBlock.end)180 if (181 options.deindent === true ||182 // by default, deindent unless it's script with default lang or (j/t)sx?183 (options.deindent !== false &&184 !(185 currentBlock.type === 'script' &&186 (!currentBlock.lang || /^(j|t)sx?$/.test(currentBlock.lang))187 ))188 ) {189 text = deindent(text)190 }191 // pad content so that linters and pre-processors can output correct192 // line numbers in errors and warnings193 if (currentBlock.type !== 'template' && options.pad) {194 text = padContent(currentBlock, options.pad) + text195 }196 currentBlock.content = text197 currentBlock = null198 }199 depth--200 }201202 function padContent(block: SFCBlock, pad: true | 'line' | 'space') {203 if (pad === 'space') {204 return source.slice(0, block.start).replace(replaceRE, ' ')205 } else {206 const offset = source.slice(0, block.start).split(splitRE).length207 const padChar = block.type === 'script' && !block.lang ? '//\n' : '\n'208 return Array(offset).join(padChar)209 }210 }211212 parseHTML(source, {213 warn,214 start,215 end,216 outputSourceRange: options.outputSourceRange217 })218219 return sfc220}
Findings
✓ No findings reported for this file.