(code, id)
| 27 | id: { include: VUE_ID_RE }, |
| 28 | }, |
| 29 | handler (code, id) { |
| 30 | const components = options.getComponents() |
| 31 | |
| 32 | if (!regexpMap.has(components)) { |
| 33 | const serverPlaceholderPath = resolve(distDir, 'app/components/server-placeholder') |
| 34 | const clientOnlyComponents = components |
| 35 | .filter(c => c.mode === 'client' && !components.some(other => other.mode !== 'client' && other.pascalName === c.pascalName && !other.filePath.startsWith(serverPlaceholderPath))) |
| 36 | .flatMap(c => [c.pascalName, c.kebabName.replaceAll('-', '_')]) |
| 37 | .concat(['ClientOnly', 'client_only']) |
| 38 | |
| 39 | regexpMap.set(components, [new RegExp(`(${clientOnlyComponents.join('|')})`), new RegExp(`^(${clientOnlyComponents.map(c => `(?:(?:_unref\\()?(?:_component_)?(?:Lazy|lazy_)?${c}\\)?)`).join('|')})$`), clientOnlyComponents]) |
| 40 | } |
| 41 | |
| 42 | const s = new MagicString(code) |
| 43 | |
| 44 | const [COMPONENTS_RE, COMPONENTS_IDENTIFIERS_RE] = regexpMap.get(components)! |
| 45 | if (!COMPONENTS_RE.test(code)) { return } |
| 46 | |
| 47 | const componentsToRemoveSet = new Set<string>() |
| 48 | |
| 49 | // remove client only components or components called in ClientOnly default slot |
| 50 | const { program: ast } = parseAndWalk(code, id, (node) => { |
| 51 | if (!isSsrRender(node)) { |
| 52 | return |
| 53 | } |
| 54 | |
| 55 | const [componentCall, _, children] = node.arguments |
| 56 | if (!componentCall) { return } |
| 57 | |
| 58 | if (componentCall.type === 'Identifier' || componentCall.type === 'MemberExpression' || componentCall.type === 'CallExpression') { |
| 59 | const componentName = getComponentName(node) |
| 60 | if (!componentName || !COMPONENTS_IDENTIFIERS_RE.test(componentName) || children?.type !== 'ObjectExpression') { return } |
| 61 | |
| 62 | const isClientOnlyComponent = CLIENT_ONLY_NAME_RE.test(componentName) |
| 63 | const slotsToRemove = isClientOnlyComponent ? children.properties.filter(prop => prop.type === 'Property' && prop.key.type === 'Identifier' && !PLACEHOLDER_EXACT_RE.test(prop.key.name)) : children.properties |
| 64 | |
| 65 | for (const slot of slotsToRemove) { |
| 66 | s.remove(slot.start, slot.end + 1) |
| 67 | const removedCode = `({${code.slice(slot.start, slot.end + 1)}})` |
| 68 | const currentState = s.toString() |
| 69 | |
| 70 | parseAndWalk(removedCode, id, (node) => { |
| 71 | if (!isSsrRender(node)) { return } |
| 72 | const name = getComponentName(node) |
| 73 | if (!name) { return } |
| 74 | |
| 75 | // detect if the component is called else where |
| 76 | const nameToRemove = isComponentNotCalledInSetup(currentState, id, name) |
| 77 | if (nameToRemove) { |
| 78 | componentsToRemoveSet.add(nameToRemove) |
| 79 | } |
| 80 | }) |
| 81 | } |
| 82 | } |
| 83 | }) |
| 84 | |
| 85 | const componentsToRemove = [...componentsToRemoveSet] |
| 86 | const removedNodes = new WeakSet<Node>() |
nothing calls this directly
no test coverage detected
searching dependent graphs…