solid-refresh
Advanced tools
Comparing version 0.3.1 to 0.3.2
89
babel.js
@@ -76,5 +76,5 @@ 'use strict'; | ||
} | ||
function createStandardHot(path, hooks, opts, HotComponent, rename) { | ||
const HotImport = getSolidRefreshIdentifier(hooks, path, opts.bundler || 'standard'); | ||
const pathToHot = getHotIdentifier(opts.bundler); | ||
function createStandardHot(path, state, HotComponent, rename) { | ||
const HotImport = getSolidRefreshIdentifier(state.hooks, path, state.opts.bundler || 'standard'); | ||
const pathToHot = getHotIdentifier(state.opts.bundler); | ||
const statementPath = getStatementPath(path); | ||
@@ -87,9 +87,9 @@ if (statementPath) { | ||
t__namespace.stringLiteral(HotComponent.name), | ||
t__namespace.stringLiteral(createSignature(rename)), | ||
state.granular.value ? t__namespace.stringLiteral(createSignature(rename)) : t__namespace.identifier('undefined'), | ||
pathToHot, | ||
]); | ||
} | ||
function createESMHot(path, hooks, opts, HotComponent, rename) { | ||
const HotImport = getSolidRefreshIdentifier(hooks, path, opts.bundler || 'standard'); | ||
const pathToHot = getHotIdentifier(opts.bundler); | ||
function createESMHot(path, state, HotComponent, rename) { | ||
const HotImport = getSolidRefreshIdentifier(state.hooks, path, state.opts.bundler || 'standard'); | ||
const pathToHot = getHotIdentifier(state.opts.bundler); | ||
const handlerId = path.scope.generateUidIdentifier("handler"); | ||
@@ -99,8 +99,12 @@ const componentId = path.scope.generateUidIdentifier("Component"); | ||
if (statementPath) { | ||
const registrationMap = createHotMap(hooks, statementPath, '$$registrations'); | ||
const registrationMap = createHotMap(state.hooks, statementPath, '$$registrations'); | ||
statementPath.insertBefore(rename); | ||
statementPath.insertBefore(t__namespace.expressionStatement(t__namespace.assignmentExpression('=', t__namespace.memberExpression(registrationMap, HotComponent), t__namespace.objectExpression([ | ||
t__namespace.objectProperty(t__namespace.identifier('component'), HotComponent), | ||
t__namespace.objectProperty(t__namespace.identifier('signature'), t__namespace.stringLiteral(createSignature(rename))), | ||
])))); | ||
statementPath.insertBefore(t__namespace.expressionStatement(t__namespace.assignmentExpression('=', t__namespace.memberExpression(registrationMap, HotComponent), t__namespace.objectExpression(state.granular.value | ||
? [ | ||
t__namespace.objectProperty(t__namespace.identifier('component'), HotComponent), | ||
t__namespace.objectProperty(t__namespace.identifier('signature'), t__namespace.stringLiteral(createSignature(rename))), | ||
] | ||
: [ | ||
t__namespace.objectProperty(t__namespace.identifier('component'), HotComponent), | ||
])))); | ||
statementPath.insertBefore(t__namespace.variableDeclaration("const", [ | ||
@@ -113,3 +117,5 @@ t__namespace.variableDeclarator(t__namespace.objectPattern([ | ||
t__namespace.stringLiteral(HotComponent.name), | ||
t__namespace.memberExpression(t__namespace.memberExpression(registrationMap, HotComponent), t__namespace.identifier('signature')), | ||
state.granular.value | ||
? t__namespace.memberExpression(t__namespace.memberExpression(registrationMap, HotComponent), t__namespace.identifier('signature')) | ||
: t__namespace.identifier('undefined'), | ||
t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", pathToHot)) | ||
@@ -122,5 +128,5 @@ ])) | ||
} | ||
function createHot(path, hooks, opts, name, expression) { | ||
if (opts.bundler === "vite") | ||
opts.bundler = "esm"; | ||
function createHot(path, state, name, expression) { | ||
if (state.opts.bundler === "vite") | ||
state.opts.bundler = "esm"; | ||
const HotComponent = name | ||
@@ -132,6 +138,6 @@ ? path.scope.generateUidIdentifier(`Hot$$${name.name}`) | ||
]); | ||
if (opts.bundler === "esm") { | ||
return createESMHot(path, hooks, opts, HotComponent, rename); | ||
if (state.opts.bundler === "esm") { | ||
return createESMHot(path, state, HotComponent, rename); | ||
} | ||
return createStandardHot(path, hooks, opts, HotComponent, rename); | ||
return createStandardHot(path, state, HotComponent, rename); | ||
} | ||
@@ -146,8 +152,15 @@ function solidRefreshPlugin() { | ||
}; | ||
this.granular = { | ||
value: false, | ||
}; | ||
}, | ||
visitor: { | ||
Program(path, { opts, processed }) { | ||
Program(path, { opts, processed, granular }) { | ||
const comments = path.hub.file.ast.comments; | ||
for (let i = 0; i < comments.length; i++) { | ||
const comment = comments[i].value; | ||
if (/^\s*@refresh granular\s*$/.test(comment)) { | ||
granular.value = true; | ||
return; | ||
} | ||
if (/^\s*@refresh skip\s*$/.test(comment)) { | ||
@@ -167,4 +180,4 @@ processed.value = true; | ||
}, | ||
ExportNamedDeclaration(path, { opts, hooks, processed }) { | ||
if (processed.value) { | ||
ExportNamedDeclaration(path, state) { | ||
if (state.processed.value) { | ||
return; | ||
@@ -174,3 +187,7 @@ } | ||
// Check if declaration is FunctionDeclaration | ||
if (t__namespace.isFunctionDeclaration(decl) && !(decl.generator || decl.async)) { | ||
if (t__namespace.isFunctionDeclaration(decl) | ||
&& !(decl.generator || decl.async) | ||
// Might be component-like, but the only valid components | ||
// have zero or one parameter | ||
&& decl.params.length < 2) { | ||
// Check if the declaration has an identifier, and then check | ||
@@ -180,3 +197,3 @@ // if the name is component-ish | ||
path.node.declaration = t__namespace.variableDeclaration('const', [ | ||
t__namespace.variableDeclarator(decl.id, createHot(path, hooks, opts, decl.id, t__namespace.functionExpression(decl.id, decl.params, decl.body))) | ||
t__namespace.variableDeclarator(decl.id, createHot(path, state, decl.id, t__namespace.functionExpression(decl.id, decl.params, decl.body))) | ||
]); | ||
@@ -186,5 +203,5 @@ } | ||
}, | ||
VariableDeclarator(path, { opts, hooks, processed }) { | ||
VariableDeclarator(path, state) { | ||
var _a, _b; | ||
if (processed.value) { | ||
if (state.processed.value) { | ||
return; | ||
@@ -205,9 +222,12 @@ } | ||
// Check for valid ArrowFunctionExpression | ||
|| (t__namespace.isArrowFunctionExpression(init) && !(init.async || init.generator)))) { | ||
path.node.init = createHot(path, hooks, opts, identifier, init); | ||
|| (t__namespace.isArrowFunctionExpression(init) && !(init.async || init.generator))) | ||
// Might be component-like, but the only valid components | ||
// have zero or one parameter | ||
&& init.params.length < 2) { | ||
path.node.init = createHot(path, state, identifier, init); | ||
} | ||
} | ||
}, | ||
FunctionDeclaration(path, { opts, hooks, processed }) { | ||
if (processed.value) { | ||
FunctionDeclaration(path, state) { | ||
if (state.processed.value) { | ||
return; | ||
@@ -221,7 +241,10 @@ } | ||
// Check if declaration is FunctionDeclaration | ||
if (!(decl.generator || decl.async)) { | ||
if (!(decl.generator || decl.async) | ||
// Might be component-like, but the only valid components | ||
// have zero or one parameter | ||
&& decl.params.length < 2) { | ||
// Check if the declaration has an identifier, and then check | ||
// if the name is component-ish | ||
if (decl.id && isComponentishName(decl.id.name)) { | ||
const replacement = createHot(path, hooks, opts, decl.id, t__namespace.functionExpression(decl.id, decl.params, decl.body)); | ||
const replacement = createHot(path, state, decl.id, t__namespace.functionExpression(decl.id, decl.params, decl.body)); | ||
if (t__namespace.isExportDefaultDeclaration(path.parentPath.node)) { | ||
@@ -241,3 +264,3 @@ path.replaceWith(replacement); | ||
&& t__namespace.isExportDefaultDeclaration(path.parentPath.node)) { | ||
const replacement = createHot(path, hooks, opts, undefined, t__namespace.functionExpression(null, decl.params, decl.body)); | ||
const replacement = createHot(path, state, undefined, t__namespace.functionExpression(null, decl.params, decl.body)); | ||
path.replaceWith(replacement); | ||
@@ -244,0 +267,0 @@ } |
@@ -10,18 +10,35 @@ 'use strict'; | ||
const [comp, setComp] = solidJs.createSignal(Comp); | ||
const [sign, setSign] = solidJs.createSignal(initialSignature); | ||
const prev = hot.data; | ||
if (prev && prev[id] /*&& prev[id].sign() !== initialSignature*/) { | ||
prev[id].setSign(() => initialSignature); | ||
prev[id].setComp(() => Comp); | ||
if (initialSignature) { | ||
const [sign, setSign] = solidJs.createSignal(initialSignature); | ||
if (prev && prev[id] && prev[id].sign() !== initialSignature) { | ||
prev[id].setSign(() => initialSignature); | ||
prev[id].setComp(() => Comp); | ||
} | ||
hot.dispose(data => { | ||
data[id] = prev ? prev[id] : { | ||
setComp, | ||
sign, | ||
setSign, | ||
}; | ||
}); | ||
} | ||
hot.dispose(data => { | ||
data[id] = prev ? prev[id] : { | ||
setComp, | ||
sign, | ||
setSign, | ||
}; | ||
}); | ||
else { | ||
if (prev && prev[id]) { | ||
prev[id].setComp(() => Comp); | ||
} | ||
hot.dispose(data => { | ||
data[id] = prev ? prev[id] : { | ||
setComp, | ||
}; | ||
}); | ||
} | ||
hot.accept(); | ||
let c; | ||
return new Proxy((props) => solidJs.createMemo(() => (c = comp()) && solidJs.untrack(() => c(props))), { | ||
return new Proxy((props) => (solidJs.createMemo(() => { | ||
const c = comp(); | ||
if (c) { | ||
return solidJs.untrack(() => c(props)); | ||
} | ||
return undefined; | ||
})), { | ||
get(_, property) { | ||
@@ -40,17 +57,29 @@ return comp()[property]; | ||
registration.component.setComp = Comp.setComp; | ||
registration.component.setSign = Comp.setSign; | ||
registration.component.sign = Comp.sign; | ||
//if (registration.signature !== Comp.sign()) { | ||
Comp.setSign(() => registration.signature); | ||
Comp.setComp(() => registration.component); | ||
//} | ||
if (initialSignature) { | ||
registration.component.setSign = Comp.setSign; | ||
registration.component.sign = Comp.sign; | ||
if (registration.signature !== Comp.sign()) { | ||
Comp.setSign(() => registration.signature); | ||
Comp.setComp(() => registration.component); | ||
} | ||
} | ||
else { | ||
Comp.setComp(() => registration.component); | ||
} | ||
} | ||
if (isHot) { | ||
const [comp, setComp] = solidJs.createSignal(Comp); | ||
const [signature, setSignature] = solidJs.createSignal(initialSignature); | ||
Comp.setComp = setComp; | ||
Comp.setSign = setSignature; | ||
Comp.sign = signature; | ||
let c; | ||
Component = new Proxy((props) => solidJs.createMemo(() => (c = comp()) && solidJs.untrack(() => c(props))), { | ||
if (initialSignature) { | ||
const [signature, setSignature] = solidJs.createSignal(initialSignature); | ||
Comp.setSign = setSignature; | ||
Comp.sign = signature; | ||
} | ||
Component = new Proxy((props) => (solidJs.createMemo(() => { | ||
const c = comp(); | ||
if (c) { | ||
return solidJs.untrack(() => c(props)); | ||
} | ||
return undefined; | ||
})), { | ||
get(_, property) { | ||
@@ -57,0 +86,0 @@ return comp()[property]; |
@@ -6,3 +6,3 @@ { | ||
"license": "MIT", | ||
"version": "0.3.1", | ||
"version": "0.3.2", | ||
"homepage": "https://github.com/solidjs/solid-refresh#readme", | ||
@@ -9,0 +9,0 @@ "repository": { |
@@ -35,3 +35,3 @@ # Solid Refresh | ||
The components are wrapped and memoized. When the module receives an update, it tries to detect if the component's content has changed between updates, prompting a remount for the changed component (which allows the ancestor components to retain their lifecycle.). | ||
The components are wrapped and memoized. When the module receives an update, it replaces the old components from the old module with the new components. | ||
@@ -51,1 +51,14 @@ ## Pragma | ||
``` | ||
### `@refresh granular` | ||
By default, components from the old module are replaced with the new ones from the replacement module, which might cause components that hasn't really changed to unmount abruptly. | ||
Adding `@refresh granular` comment pragma in the file allows components to opt-in to granular replacement: If the component has changed *code-wise*, it will be replaced, otherwise, it will be retained, which allows unchanged ancestor components to preserve lifecycles. | ||
The downside of this mode is that local bindings that are part of the module that which the components depends on won't be detected as a change and thus will not trigger a replacement. This also means that the component may be accessing a different instance of that binding (e.g. createContext). This is currently a known limitation. | ||
## Limitations | ||
- Preserving state: The default mode does not allow preserving state through module replacement. `@refresh granular` allows this partially. | ||
- No HOC support. |
Sorry, the diff of this file is not supported yet
22268
425
63