Skip to content

Commit 46652bc

Browse files
committed
feat: support browser
1 parent d19a4f2 commit 46652bc

File tree

11 files changed

+92
-37
lines changed

11 files changed

+92
-37
lines changed

.github/workflows/unit-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
strategy:
3535
matrix:
3636
os: [ubuntu-latest, windows-latest]
37-
node: [18, 20]
37+
node: [20, 22]
3838
fail-fast: false
3939

4040
steps:

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
"require": "./dist/volar.cjs",
5353
"import": "./dist/volar.js"
5454
},
55+
"./volar-browser": {
56+
"require": "./dist/volar-browser.cjs",
57+
"import": "./dist/volar-browser.js"
58+
},
5559
"./raw": {
5660
"require": "./dist/raw.cjs",
5761
"import": "./dist/raw.js"

playground/src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="tsx">
2-
import { type Ref, inject, provide, toRefs, watch } from 'vue'
2+
import { type Ref, inject, provide, ref, toRefs, watch } from 'vue'
33
import { useBase64 } from '@vueuse/core'
44
import { useUserStore } from '../store/user'
55

playground/src/vite-env.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
/// <reference types="vite/client" />
2-
/// <reference types="vue/macros-global" />

src/core/utils.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable node/prefer-global/process */
12
import { walk } from 'estree-walker'
23
import type { Node } from 'oxc-parser'
34
import type { Reference, Scope } from '@typescript-eslint/scope-manager'
@@ -65,3 +66,32 @@ export function transformFunctionReturn(node: Node, refs: Node[]) {
6566
}
6667
}
6768
}
69+
70+
let require: NodeJS.Require | undefined
71+
export function getRequire() {
72+
if (require) return require
73+
74+
try {
75+
// @ts-expect-error check api
76+
if (globalThis.process?.getBuiltinModule) {
77+
const module = process.getBuiltinModule('module')
78+
// unenv has implemented `getBuiltinModule` but has yet to support `module.createRequire`
79+
if (module?.createRequire) {
80+
return (require = module.createRequire(import.meta.url))
81+
}
82+
}
83+
} catch {}
84+
}
85+
86+
let parseSync: typeof import('oxc-parser').parseSync
87+
export async function getOxcParser() {
88+
if (!parseSync) {
89+
parseSync = await import(
90+
// @ts-ignore
91+
typeof window !== 'undefined'
92+
? 'https://cdn.jsdelivr.net/npm/@oxc-parser/binding-wasm32-wasi/browser-bundle.mjs'
93+
: 'oxc-parser'
94+
).then((mod) => mod.parseSync)
95+
}
96+
return parseSync
97+
}

src/global.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
declare module 'estree-walker' {
2+
export function walk<T>(
3+
root: T,
4+
options: {
5+
enter?: (node: T, parent: T | null) => any
6+
leave?: (node: T, parent: T | null) => any
7+
exit?: (node: T) => any
8+
} & ThisType<{ skip: () => void }>,
9+
)
10+
}
11+
12+
declare let __BROWSER__: boolean

src/raw.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,38 @@ import {
1010
parseSFC,
1111
} from '@vue-macros/common'
1212
import { analyze } from '@typescript-eslint/scope-manager'
13-
import { type IdentifierName, type Node, parseSync } from 'oxc-parser'
14-
// @ts-ignore
1513
import { walk } from 'estree-walker'
1614
import { type Options, resolveOption } from './core/options'
1715
import {
1816
collectRefs,
17+
getOxcParser,
1918
getReferences,
2019
transformFunctionReturn,
2120
} from './core/utils'
21+
import type { IdentifierName, Node } from 'oxc-parser'
2222
import type { UnpluginOptions } from 'unplugin'
2323

24-
export function transformReactivityFunction(
24+
export async function transformReactivityFunction(
2525
code: string,
2626
ignore: string[],
2727
s: MagicStringAST,
2828
) {
29+
const parseSync = await getOxcParser()
2930
const { program } = parseSync('index.tsx', code, {
3031
sourceType: 'module',
3132
})
3233

3334
const unrefs: IdentifierName[] = []
3435
const refs: Node[] = []
3536
let index = 0
36-
walk(program, {
37-
leave(node: Node, parent: Node) {
37+
walk<Node>(program, {
38+
leave(node, parent) {
3839
// @ts-ignore
3940
node.parent = parent
4041
// @ts-ignore
4142
node.range = [node.start, node.end]
4243
},
43-
enter(node: Node, parent: Node) {
44+
enter(node, parent) {
4445
if (node.type === 'TSNonNullExpression') {
4546
node = node.expression
4647
}

src/volar.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import {
55
replaceSourceRange,
66
} from 'ts-macro'
77
import { analyze } from '@typescript-eslint/scope-manager'
8-
// @ts-ignore
98
import { walk } from 'estree-walker'
10-
import { type IdentifierName, type Node, parseSync } from 'oxc-parser'
119
import {
1210
collectRefs,
11+
getOxcParser,
1312
getReferences,
13+
getRequire,
1414
transformFunctionReturn,
1515
} from './core/utils'
16+
import type { IdentifierName, Node } from 'oxc-parser'
1617

1718
const HELPER_PREFIX = '__MACROS_'
1819

@@ -28,7 +29,7 @@ const plugin = createPlugin<{ ignore?: string[] } | undefined>(
2829
const ignore = (options?.ignore || []).map((str) => str.slice(1))
2930
return {
3031
name: 'vue-reactivity-function',
31-
resolveVirtualCode({ filePath, ast, source, codes }) {
32+
resolveVirtualCode({ ast, source, codes }) {
3233
if (!ast.text.includes('$')) return
3334
try {
3435
transformReactivityFunction({
@@ -45,6 +46,16 @@ const plugin = createPlugin<{ ignore?: string[] } | undefined>(
4546

4647
export default plugin
4748

49+
let parseSync: typeof import('oxc-parser').parseSync
50+
if (__BROWSER__) {
51+
parseSync = await getOxcParser()
52+
} else {
53+
const require = getRequire()
54+
if (require) {
55+
parseSync = require('oxc-parser').parseSync
56+
}
57+
}
58+
4859
function transformReactivityFunction(options: {
4960
codes: Code[]
5061
source?: string
@@ -58,14 +69,14 @@ function transformReactivityFunction(options: {
5869
const unrefs: IdentifierName[] = []
5970
const refs: Node[] = []
6071
let index = 0
61-
walk(program, {
62-
leave(node: Node, parent: Node) {
72+
walk<Node>(program, {
73+
leave(node, parent) {
6374
// @ts-ignore
6475
node.parent = parent
6576
// @ts-ignore
6677
node.range = [node.start, node.end]
6778
},
68-
enter(node: Node, parent: Node) {
79+
enter(node, parent) {
6980
if (node.type === 'TSNonNullExpression') {
7081
node = node.expression
7182
}

tsdown.config.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
import { defineConfig } from 'tsdown'
22

3-
export default defineConfig({
4-
entry: ['./src/*.ts', '!./src/*.d.ts'],
5-
format: ['cjs', 'esm'],
6-
dts: true,
7-
outputOptions: {
8-
exports: 'named',
3+
export default defineConfig([
4+
{
5+
define: {
6+
__BROWSER__: 'true',
7+
},
8+
entry: {
9+
'volar-browser': './src/volar.ts',
10+
},
11+
format: 'esm',
12+
dts: true,
913
},
10-
})
14+
{
15+
define: {
16+
__BROWSER__: 'false',
17+
},
18+
entry: ['./src/*.ts', '!./src/*.d.ts'],
19+
format: ['cjs', 'esm'],
20+
dts: true,
21+
},
22+
])

tsm.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import reactivityFunction from './src/volar'
1+
import reactivityFunction from './dist/volar.js'
22

33
export default {
44
plugins: [reactivityFunction()],

tsup.config.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)