Skip to content

Commit

Permalink
feat(volar/export-expose): support export function & class & `mo…
Browse files Browse the repository at this point in the history
…dule` (#646)

* feat(volar/export-expose): support export `function` & `class` & `module`

* chore: add changeset

* fix: lint
  • Loading branch information
zhiyuanzmj committed Mar 22, 2024
1 parent a22fa9d commit e15f572
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changeset/sharp-elephants-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vue-macros/volar": patch
---

support export `function` & `class` & `module` for export-expose
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ exports[`fixtures > ./fixtures/basic.vue 1`] = `
bar = 10
let baz: string | undefined
var qux = fn()
const { a, b, c } = { a: 1, b: 2, c: 3 }
const { a: a1, b, c } = { a: 1, b: 2, c: 3 }
function fn() {}
class A {}
Expand All @@ -17,7 +17,7 @@ defineExpose({
bar,
baz,
qux,
a,
a1,
b,
c,
fn,
Expand Down Expand Up @@ -58,7 +58,9 @@ exports[`fixtures > ./fixtures/rename.vue 1`] = `
const foo = 1,
bar = 1
// @ts-expect-error
import { foo as __MACROS_expose_0 } from './types'
// @ts-expect-error
import * as __MACROS_expose_1 from './types'
defineExpose({
foo: __MACROS_expose_0,
Expand Down
4 changes: 2 additions & 2 deletions packages/export-expose/tests/fixtures/basic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ export const foo: string = 'foo',
bar = 10
export let baz: string | undefined
export var qux = fn()
export const { a, b, c } = { a: 1, b: 2, c: 3 }
export const { a: a1, b, c } = { a: 1, b: 2, c: 3 }
export function fn() {}
export class A {}
export { foo as foo1 }
export { foo as foo1, bar }
</script>
2 changes: 2 additions & 0 deletions packages/export-expose/tests/fixtures/rename.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
const foo = 1,
bar = 1
// @ts-expect-error
export { foo } from './types'
// @ts-expect-error
export * as bar from './types'
</script>
143 changes: 124 additions & 19 deletions packages/volar/src/export-expose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,131 @@ function transform({
)
if (!filter(fileName)) return

const exposed: Record<string, Segment<FileRangeCapabilities>> = Object.create(
null,
)
const exposed: Record<string, string> = Object.create(null)
for (const stmt of sfc.scriptSetup!.ast.statements) {
if (!ts.isVariableStatement(stmt)) continue
const exportModifier = stmt.modifiers?.find(
(m) => m.kind === ts.SyntaxKind.ExportKeyword,
)
if (!exportModifier) continue

const start = exportModifier.getStart(sfc.scriptSetup?.ast)
const end = exportModifier.getEnd()
replaceSourceRange(file.content, 'scriptSetup', start, end)

for (const decl of stmt.declarationList.declarations) {
if (!ts.isIdentifier(decl.name)) continue
const name = decl.name.text
const start = decl.name.getStart(sfc.scriptSetup?.ast)

exposed[name] = [name, 'scriptSetup', start, FileRangeCapabilities.full]
if (ts.isExportDeclaration(stmt) && stmt.exportClause) {
if (ts.isNamedExports(stmt.exportClause)) {
const exportMap = new Map<
Segment<FileRangeCapabilities>,
Segment<FileRangeCapabilities>
>()
stmt.exportClause.elements.forEach((element) => {
if (element.isTypeOnly) return

const name = element.name
const propertyName = element.propertyName || name

exportMap.set(
[
propertyName.text,
'scriptSetup',
propertyName.getStart(sfc.scriptSetup?.ast),
FileRangeCapabilities.full,
],
[
name.text,
'scriptSetup',
name.getStart(sfc.scriptSetup?.ast),
FileRangeCapabilities.full,
],
)

exposed[name.text] = propertyName.text
})

if (stmt.moduleSpecifier) {
const start = stmt.getStart(sfc.scriptSetup?.ast)
const end = stmt.getEnd()

replaceSourceRange(
file.content,
'scriptSetup',
start,
start + 6,
'import',
)
replaceSourceRange(
file.content,
'scriptSetup',
end,
end,
`;[${Array.from(exportMap.values()).map(([name]) => name)}];`,
)
} else {
replaceSourceRange(
file.content,
'scriptSetup',
stmt.getStart(sfc.scriptSetup?.ast),
stmt.getEnd(),
`(({`,
...Array.from(exportMap.entries()).flatMap(([name, value]) =>
name[0] === value[0] ? [value, ','] : [name, ':', value, ','],
),
`})=>{${Array.from(exportMap.values()).map(([name]) => name)}`,
`})({${Array.from(exportMap.keys()).map(([name]) => name)}})`,
)
}
} else if (ts.isNamespaceExport(stmt.exportClause)) {
const start = stmt.getStart(sfc.scriptSetup?.ast)
const end = stmt.getEnd()

replaceSourceRange(
file.content,
'scriptSetup',
start,
start + 6,
'import',
)
replaceSourceRange(
file.content,
'scriptSetup',
end,
end,
`;[${stmt.exportClause.name.text}];`,
)
}
} else if (
ts.isVariableStatement(stmt) ||
ts.isFunctionDeclaration(stmt) ||
ts.isClassDeclaration(stmt)
) {
const exportModifier = stmt.modifiers?.find(
(m) => m.kind === ts.SyntaxKind.ExportKeyword,
)
if (!exportModifier) continue

const exposedValues: string[] = []
if (ts.isVariableStatement(stmt)) {
for (const decl of stmt.declarationList.declarations) {
if (!decl.name) continue

if (ts.isIdentifier(decl.name)) {
const name = decl.name.text
exposed[name] = name
} else if (ts.isObjectBindingPattern(decl.name)) {
decl.name.elements.forEach((element) => {
if (!ts.isIdentifier(element.name)) return

exposedValues.push(element.name.text)
exposed[element.name.text] =
element.propertyName && ts.isIdentifier(element.propertyName)
? element.propertyName.text
: element.name.text
})
}
}
} else if (stmt.name && ts.isIdentifier(stmt.name)) {
const name = stmt.name.text
exposed[name] = name
}

replaceSourceRange(
file.content,
'scriptSetup',
exportModifier.getStart(sfc.scriptSetup?.ast),
exportModifier.getEnd(),
exposedValues.length > 0 ? `[${exposedValues}];` : '',
)
}
}

Expand Down
7 changes: 7 additions & 0 deletions tsconfig.fixture.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@
"@vue-macros/volar/short-vmodel",
"@vue-macros/volar/define-slots",
"@vue-macros/volar/export-props",
"@vue-macros/volar/export-expose",
"@vue-macros/volar/export-render",
"@vue-macros/volar/jsx-directive"
],
"vueMacros": {
"shortVmodel": {
"prefix": "$"
},
"exportProps": {
"include": ["**/export-props/**"]
},
"exportExpose": {
"include": ["**/export-expose/**"]
}
}
}
Expand Down

0 comments on commit e15f572

Please sign in to comment.