forked from pmndrs/jotai
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplugin-react-refresh.ts
80 lines (76 loc) · 2.68 KB
/
plugin-react-refresh.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import babel from '@babel/core'
import type { PluginObj } from '@babel/core'
import _templateBuilder from '@babel/template'
import { isAtom } from './utils.ts'
import type { PluginOptions } from './utils.ts'
const templateBuilder = (_templateBuilder as any).default || _templateBuilder
export default function reactRefreshPlugin(
{ types: t }: typeof babel,
options?: PluginOptions,
): PluginObj {
return {
pre({ opts }) {
if (!opts.filename) {
throw new Error('Filename must be available')
}
},
visitor: {
Program: {
exit(path) {
const jotaiAtomCache = templateBuilder(`
globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {
cache: new Map(),
get(name, inst) {
if (this.cache.has(name)) {
return this.cache.get(name)
}
this.cache.set(name, inst)
return inst
},
}`)()
path.unshiftContainer('body', jotaiAtomCache)
},
},
ExportDefaultDeclaration(nodePath, state) {
const { node } = nodePath
if (
t.isCallExpression(node.declaration) &&
isAtom(t, node.declaration.callee, options?.customAtomNames)
) {
const filename = state.filename || 'unknown'
const atomKey = `${filename}/defaultExport`
const buildExport = templateBuilder(
`export default globalThis.jotaiAtomCache.get(%%atomKey%%, %%atom%%)`,
)
const ast = buildExport({
atomKey: t.stringLiteral(atomKey),
atom: node.declaration,
})
nodePath.replaceWith(ast as babel.Node)
}
},
VariableDeclarator(nodePath, state) {
if (
t.isIdentifier(nodePath.node.id) &&
t.isCallExpression(nodePath.node.init) &&
isAtom(t, nodePath.node.init.callee, options?.customAtomNames) &&
// Make sure atom declaration is in module scope
(nodePath.parentPath.parentPath?.isProgram() ||
nodePath.parentPath.parentPath?.isExportNamedDeclaration())
) {
const filename = state.filename || 'unknown'
const atomKey = `${filename}/${nodePath.node.id.name}`
const buildAtomDeclaration = templateBuilder(
`const %%atomIdentifier%% = globalThis.jotaiAtomCache.get(%%atomKey%%, %%atom%%)`,
)
const ast = buildAtomDeclaration({
atomIdentifier: t.identifier(nodePath.node.id.name),
atomKey: t.stringLiteral(atomKey),
atom: nodePath.node.init,
})
nodePath.parentPath.replaceWith(ast as babel.Node)
}
},
},
}
}