UNPKG

2.83 kBJavaScriptView Raw
1import templateBuilder from '@babel/template';
2
3function isAtom(t, callee) {
4 if (t.isIdentifier(callee) && atomFunctionNames.includes(callee.name)) {
5 return true;
6 }
7 if (t.isMemberExpression(callee)) {
8 const { property } = callee;
9 if (t.isIdentifier(property) && atomFunctionNames.includes(property.name)) {
10 return true;
11 }
12 }
13 return false;
14}
15const atomFunctionNames = [
16 "atom",
17 "atomFamily",
18 "atomWithDefault",
19 "atomWithObservable",
20 "atomWithReducer",
21 "atomWithReset",
22 "atomWithStorage",
23 "freezeAtom",
24 "loadable",
25 "selectAtom",
26 "splitAtom"
27];
28
29function reactRefreshPlugin({
30 types: t
31}) {
32 return {
33 pre({ opts }) {
34 if (!opts.filename) {
35 throw new Error("Filename must be available");
36 }
37 },
38 visitor: {
39 Program: {
40 exit(path) {
41 const jotaiAtomCache = templateBuilder(`
42 globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {
43 cache: new Map(),
44 get(name, inst) {
45 if (this.cache.has(name)) {
46 return this.cache.get(name)
47 }
48 this.cache.set(name, inst)
49 return inst
50 },
51 }`)();
52 path.unshiftContainer("body", jotaiAtomCache);
53 }
54 },
55 ExportDefaultDeclaration(nodePath, state) {
56 const { node } = nodePath;
57 if (t.isCallExpression(node.declaration) && isAtom(t, node.declaration.callee)) {
58 const filename = state.filename || "unknown";
59 const atomKey = `${filename}/defaultExport`;
60 const buildExport = templateBuilder(`export default globalThis.jotaiAtomCache.get(%%atomKey%%, %%atom%%)`);
61 const ast = buildExport({
62 atomKey: t.stringLiteral(atomKey),
63 atom: node.declaration
64 });
65 nodePath.replaceWith(ast);
66 }
67 },
68 VariableDeclarator(nodePath, state) {
69 var _a, _b;
70 if (t.isIdentifier(nodePath.node.id) && t.isCallExpression(nodePath.node.init) && isAtom(t, nodePath.node.init.callee) && (((_a = nodePath.parentPath.parentPath) == null ? void 0 : _a.isProgram()) || ((_b = nodePath.parentPath.parentPath) == null ? void 0 : _b.isExportNamedDeclaration()))) {
71 const filename = state.filename || "unknown";
72 const atomKey = `${filename}/${nodePath.node.id.name}`;
73 const buildAtomDeclaration = templateBuilder(`const %%atomIdentifier%% = globalThis.jotaiAtomCache.get(%%atomKey%%, %%atom%%)`);
74 const ast = buildAtomDeclaration({
75 atomIdentifier: t.identifier(nodePath.node.id.name),
76 atomKey: t.stringLiteral(atomKey),
77 atom: nodePath.node.init
78 });
79 nodePath.parentPath.replaceWith(ast);
80 }
81 }
82 }
83 };
84}
85
86export { reactRefreshPlugin as default };