UNPKG

@vuedx/compiler-sfc

Version:

This package extracts `parse()` function [@vue/compiler-sfc] as ES module.

203 lines (200 loc) 7.01 kB
import { parse as parse$1 } from '@vue/compiler-dom'; import { first, last } from '@vuedx/shared'; import { NodeTypes } from '@vuedx/template-ast-types'; let isParsingBrokenTemplate = false; function parse(source, compiler = { parse: parse$1, }) { const descriptor = { script: null, scriptSetup: null, template: null, styles: [], customBlocks: [], }; const errors = []; const ast = compiler.parse(source, { // there are no components at SFC parsing level isNativeTag: () => true, decodeEntities: (string) => string, // preserve all whitespace isPreTag: () => true, getTextMode: ({ tag, props }, parent) => { // all top level elements except <template> are parsed as raw text // containers if ((parent == null && 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 != null && p.value.content.length > 0 && p.value.content !== 'html'))) { return 2; /* RAWTEXT */ } else { return 0; /* DATA */ } }, onError: (e) => { errors.push(e); }, }); ast.children.forEach((node) => { if (node.type !== 1 /* ELEMENT */) { return; } switch (node.tag) { case 'template': if (descriptor.template == null) { descriptor.template = createBlock(node, source); } else { errors.push(createDuplicateBlockError(node)); } break; case 'script': { const scriptBlock = createBlock(node, source); const isSetup = scriptBlock.attrs['setup'] != null; if (isSetup) { if (descriptor.scriptSetup == null) { descriptor.scriptSetup = scriptBlock; } else { errors.push(createDuplicateBlockError(node, isSetup)); } } else { if (descriptor.script == null) { descriptor.script = scriptBlock; } else { errors.push(createDuplicateBlockError(node, isSetup)); } } } break; case 'style': { const styleBlock = createBlock(node, source); descriptor.styles.push(styleBlock); } break; default: descriptor.customBlocks.push(createBlock(node, source)); break; } }); if (descriptor.template != null && !isParsingBrokenTemplate) { const start = descriptor.template.loc.start.offset; const end = source.lastIndexOf('</template'); if (end < descriptor.template.loc.end.offset) { // There is an parsing error in the template. const proxy = source.slice(0, start) + placeholder(source.slice(start, end)) + source.slice(end); isParsingBrokenTemplate = true; try { const result = parse(proxy, compiler); if (result.descriptor.template != null) { result.descriptor.template.content = source.slice(start, end); result.descriptor.template.loc.source = source.slice(start, end); } return result; } finally { isParsingBrokenTemplate = false; } } } if (descriptor.scriptSetup != null) { if (descriptor.scriptSetup.src != null) { errors.push(new SyntaxError(`<script setup> cannot use the "src" attribute because ` + `its syntax will be ambiguous outside of the component.`)); descriptor.scriptSetup = null; } if (descriptor.script?.src != null) { errors.push(new SyntaxError(`<script> cannot use the "src" attribute when <script setup> is ` + `also present because they must be processed together.`)); descriptor.script = null; } } return { descriptor, errors }; } function createDuplicateBlockError(node, isScriptSetup = false) { const err = new SyntaxError(`Single file component can contain only one <${node.tag}${isScriptSetup ? ` setup` : ``}> element`); err.loc = node.loc; return err; } function createBlock(node, source) { const type = node.tag; let { start, end } = node.loc; let content = ''; if (node.children.length > 0) { start = first(node.children).loc.start; end = last(node.children).loc.end; content = source.slice(start.offset, end.offset); } else { const offset = node.loc.source.indexOf(`</`); if (offset > -1) { start = { line: start.line, column: start.column + offset, offset: start.offset + offset, }; } end = { ...start }; } const loc = { source: content, start, end, }; const attrs = {}; const block = { type, content, loc, attrs, }; node.props.forEach((p) => { if (p.type === NodeTypes.ATTRIBUTE) { attrs[p.name] = p.value != null && p.value.content.trim() !== '' ? p.value.content : true; if (p.name === 'lang') { if (p.value != null) block.lang = p.value.content; } else if (p.name === 'src') { if (p.value != null) block.src = p.value.content; } else if (type === 'style') { if (p.name === 'scoped') { block.scoped = true; } else if (p.name === 'module') { block.module = attrs[p.name]; } } else if (type === 'script' && p.name === 'setup') { block.setup = attrs['setup']; } } }); return block; } function placeholder(content) { let output = ''; const nl = '\n'.charCodeAt(0); for (let i = 0; i < content.length; i++) { output += content.charCodeAt(i) === nl ? '\n' : ' '; } return output; } export { parse }; //# sourceMappingURL=index.mjs.map