Skip to content

Commit e34ff23

Browse files
zhiyuanzmjposva
andauthored
feat(define-page): support JSX (#514)
* feat(define-page): support babel-parser * fix: typo * fix: typo * feat: support script block * chore: remove type * feat: remove vue for .tsx file * chore: up test * refactor: remove ts casts * refactor: get page nodes within getCodeAst --------- Co-authored-by: Eduardo San Martin Morote <[email protected]>
1 parent 9c969cf commit e34ff23

File tree

4 files changed

+59
-40
lines changed

4 files changed

+59
-40
lines changed

src/codegen/generateRouteRecords.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getLang } from '@vue-macros/common'
12
import type { TreeNode } from '../core/tree'
23
import { ImportsMap } from '../core/utils'
34
import { type ResolvedOptions } from '../options'
@@ -32,9 +33,11 @@ ${node
3233
for (const [name, filePath] of node.value.components) {
3334
const pageDataImport = `_definePage_${name}_${importsMap.size}`
3435
definePageDataList.push(pageDataImport)
36+
const lang = getLang(filePath)
3537
importsMap.addDefault(
3638
// TODO: apply the language used in the sfc
37-
`${filePath}?definePage&vue&lang.tsx`,
39+
`${filePath}?definePage&` +
40+
(lang === 'vue' ? 'vue&lang.tsx' : `lang.${lang}`),
3841
pageDataImport
3942
)
4043
}

src/core/__snapshots__/definePage.spec.ts.snap

+7
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,10 @@ const b = 1
7272
</template>
7373
"
7474
`;
75+
76+
exports[`definePage > works with jsx 1`] = `
77+
"export default {
78+
name: 'custom',
79+
path: '/custom',
80+
}"
81+
`;

src/core/definePage.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ definePage({
135135
})
136136
})
137137

138-
it.todo('works with jsx', async () => {
138+
it('works with jsx', async () => {
139139
const code = `
140140
const a = 1
141141
definePage({
@@ -146,11 +146,11 @@ definePage({
146146
`,
147147
result = (await definePageTransform({
148148
code,
149-
id: 'src/pages/basic.vue?definePage&jsx',
149+
id: 'src/pages/basic.jsx?definePage&lang.jsx',
150150
})) as Exclude<TransformResult, string>
151151
expect(result).toBeDefined()
152152
expect(result).toHaveProperty('code')
153-
expect(result?.code).toMatchInlineSnapshot()
153+
expect(result?.code).toMatchSnapshot()
154154
})
155155

156156
it('throws if definePage uses a variable from the setup', async () => {

src/core/definePage.ts

+45-36
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import {
44
parseSFC,
55
MagicString,
66
checkInvalidScopeReference,
7+
babelParse,
8+
getLang,
79
} from '@vue-macros/common'
810
import type { Thenable, TransformResult } from 'unplugin'
911
import type {
1012
CallExpression,
1113
Node,
1214
ObjectProperty,
15+
Program,
1316
Statement,
1417
StringLiteral,
1518
} from '@babel/types'
@@ -25,6 +28,39 @@ function isStringLiteral(node: Node | null | undefined): node is StringLiteral {
2528
return node?.type === 'StringLiteral'
2629
}
2730

31+
/**
32+
* Generate the ast from a code string and an id. Works with SFC and non-SFC files.
33+
*/
34+
function getCodeAst(code: string, id: string) {
35+
let offset = 0
36+
let ast: Program | undefined
37+
const lang = getLang(id.split(MACRO_DEFINE_PAGE_QUERY)[0]!)
38+
if (lang === 'vue') {
39+
const sfc = parseSFC(code, id)
40+
if (sfc.scriptSetup) {
41+
ast = sfc.getSetupAst()
42+
offset = sfc.scriptSetup.loc.start.offset
43+
} else if (sfc.script) {
44+
ast = sfc.getScriptAst()
45+
offset = sfc.script.loc.start.offset
46+
}
47+
} else if (/[jt]sx?$/.test(lang)) {
48+
ast = babelParse(code, lang)
49+
}
50+
51+
const definePageNodes: CallExpression[] = (ast?.body || [])
52+
.map((node) => {
53+
const definePageCallNode =
54+
node.type === 'ExpressionStatement' ? node.expression : node
55+
return isCallOf(definePageCallNode, MACRO_DEFINE_PAGE)
56+
? definePageCallNode
57+
: null
58+
})
59+
.filter((node) => !!node)
60+
61+
return { ast, offset, definePageNodes }
62+
}
63+
2864
export function definePageTransform({
2965
code,
3066
id,
@@ -41,20 +77,8 @@ export function definePageTransform({
4177
return isExtractingDefinePage ? 'export default {}' : undefined
4278
}
4379

44-
// TODO: handle also non SFC
45-
46-
const sfc = parseSFC(code, id)
47-
if (!sfc.scriptSetup) return
48-
49-
const { scriptSetup, getSetupAst } = sfc
50-
const setupAst = getSetupAst()
51-
52-
const definePageNodes = (setupAst?.body || ([] as Node[]))
53-
.map((node) => {
54-
if (node.type === 'ExpressionStatement') node = node.expression
55-
return isCallOf(node, MACRO_DEFINE_PAGE) ? node : null
56-
})
57-
.filter((node): node is CallExpression => !!node)
80+
const { ast, offset, definePageNodes } = getCodeAst(code, id)
81+
if (!ast) return
5882

5983
if (!definePageNodes.length) {
6084
return isExtractingDefinePage
@@ -67,7 +91,6 @@ export function definePageTransform({
6791
}
6892

6993
const definePageNode = definePageNodes[0]!
70-
const setupOffset = scriptSetup.loc.start.offset
7194

7295
// we only want the page info
7396
if (isExtractingDefinePage) {
@@ -82,13 +105,13 @@ export function definePageTransform({
82105
)
83106
}
84107

85-
const scriptBindings = setupAst?.body ? getIdentifiers(setupAst.body) : []
108+
const scriptBindings = ast.body ? getIdentifiers(ast.body) : []
86109

87110
// this will throw if a property from the script setup is used in definePage
88111
checkInvalidScopeReference(routeRecord, MACRO_DEFINE_PAGE, scriptBindings)
89112

90-
s.remove(setupOffset + routeRecord.end!, code.length)
91-
s.remove(0, setupOffset + routeRecord.start!)
113+
s.remove(offset + routeRecord.end!, code.length)
114+
s.remove(0, offset + routeRecord.start!)
92115
s.prepend(`export default `)
93116

94117
// find all static imports and filter out the ones that are not used
@@ -156,11 +179,8 @@ export function definePageTransform({
156179

157180
const s = new MagicString(code)
158181

159-
// s.removeNode(definePageNode, { offset: setupOffset })
160-
s.remove(
161-
setupOffset + definePageNode.start!,
162-
setupOffset + definePageNode.end!
163-
)
182+
// s.removeNode(definePageNode, { offset })
183+
s.remove(offset + definePageNode.start!, offset + definePageNode.end!)
164184

165185
return generateTransform(s, id)
166186
}
@@ -172,19 +192,8 @@ export function extractDefinePageNameAndPath(
172192
): { name?: string; path?: string } | null | undefined {
173193
if (!sfcCode.includes(MACRO_DEFINE_PAGE)) return
174194

175-
const sfc = parseSFC(sfcCode, id)
176-
177-
if (!sfc.scriptSetup) return
178-
179-
const { getSetupAst } = sfc
180-
const setupAst = getSetupAst()
181-
182-
const definePageNodes = (setupAst?.body ?? ([] as Node[]))
183-
.map((node) => {
184-
if (node.type === 'ExpressionStatement') node = node.expression
185-
return isCallOf(node, MACRO_DEFINE_PAGE) ? node : null
186-
})
187-
.filter((node): node is CallExpression => !!node)
195+
const { ast, definePageNodes } = getCodeAst(sfcCode, id)
196+
if (!ast) return
188197

189198
if (!definePageNodes.length) {
190199
return

0 commit comments

Comments
 (0)