-
Notifications
You must be signed in to change notification settings - Fork 72
Replace broken Svelte 2 HMR with the one from rixo #156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 82 commits
45f83ef
23fa354
eda88c6
5b568c8
dcf722e
9dd5eec
66b3061
87b4192
76ec3b8
c4d9f09
21e0276
733852f
b2c1d29
81cfdd2
189a246
74636fb
0089526
e106329
7787618
46db2fd
3122272
5fbd73b
75bc4a3
638aa78
67e98a8
9026759
e69f405
d6e4a1a
91cfef7
0e5b209
4c322c2
8d5701a
1ae9f83
3f54b18
fadd9b4
4e6f38a
765d64d
2204a8f
dfbdb6f
251588e
3bc4671
3278148
2be1bd7
4a20c99
ed4e5ed
e715674
66fe7ac
7929ed5
73b11bb
40068b8
1059907
962a0be
ada239e
bdb5c58
6caf00d
7783129
e2d82d9
8b31c10
b2d291c
472668f
0c1ccaa
60b7ad9
ca2e6bd
ecaa242
7367022
f148343
0d7fb49
e2be8af
5d444ba
25e3244
df80f24
4071bfc
1351638
42550eb
cd29428
e01f36a
f88b4c6
93feeb4
8b7d9bc
b5afe79
7e65739
db9ff99
780c961
931dcc9
1ff2a35
ca550ea
7925e76
0f1c49b
bfb3b47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -1,7 +1,7 @@ | ||||
const { relative } = require('path'); | ||||
const { getOptions } = require('loader-utils'); | ||||
|
||||
const hotApi = require.resolve('./lib/hot-api.js'); | ||||
const makeHot = require('./lib/make-hot.js'); | ||||
|
||||
const { compile, preprocess } = require('svelte/compiler'); | ||||
|
||||
|
@@ -20,30 +20,6 @@ const pluginOptions = { | |||
markup: true | ||||
}; | ||||
|
||||
function makeHot(id, code, hotOptions) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed that @rixo's branch does not have the below options specified in
I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where did you get this from? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should only disable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like the only thing it's used for is to determine if it's a compiler option: Line 81 in 0f1c49b
Since none of these are compiler options we should probably remove them all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is better to avoid making changes that are out of the scope of this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to clarify, when I said "all" I meant the four options I originally suggested removing: There shouldn't be any reason to pass those to the compiler since none of those four are valid compiler options: https://svelte.dev/docs#svelte_compile There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Doing this breaks tests and compilation. These options are PREVENTED from going to the compiler due to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I see. Sorry I had that backwards. It's a bit confusingly written. In There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is done. Yeah, that made me scratch my head for several minutes too. 😄 |
||||
const options = JSON.stringify(hotOptions); | ||||
const replacement = ` | ||||
if (module.hot) { | ||||
const { configure, register, reload } = require('${posixify(hotApi)}'); | ||||
|
||||
module.hot.accept(); | ||||
|
||||
if (!module.hot.data) { | ||||
// initial load | ||||
configure(${options}); | ||||
$2 = register(${id}, $2); | ||||
} else { | ||||
// hot update | ||||
$2 = reload(${id}, $2); | ||||
} | ||||
} | ||||
|
||||
export default $2; | ||||
`; | ||||
|
||||
return code.replace(/(export default ([^;]*));/, () => replacement); | ||||
} | ||||
|
||||
function posixify(file) { | ||||
return file.replace(/[/\\]/g, '/'); | ||||
} | ||||
|
@@ -120,7 +96,8 @@ module.exports = function(source, map) { | |||
} | ||||
} | ||||
|
||||
let { js, css, warnings } = normalize(compile(processed.toString(), compileOptions)); | ||||
const compiled = compile(processed.toString(), compileOptions); | ||||
let { js, css, warnings } = normalize(compiled); | ||||
|
||||
warnings.forEach( | ||||
options.onwarn | ||||
|
@@ -131,7 +108,7 @@ module.exports = function(source, map) { | |||
if (options.hotReload && !isProduction && !isServer) { | ||||
const hotOptions = Object.assign({}, options.hotOptions); | ||||
const id = JSON.stringify(relative(process.cwd(), compileOptions.filename)); | ||||
js.code = makeHot(id, js.code, hotOptions); | ||||
js.code = makeHot(id, js.code, hotOptions, compiled, source, compileOptions); | ||||
} | ||||
|
||||
if (options.emitCss && css.code) { | ||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,103 @@ | ||
import { Registry, configure as configureProxy, createProxy } from 'svelte-dev-helper'; | ||
import { makeApplyHmr } from 'svelte-hmr/runtime'; | ||
|
||
let hotOptions = { | ||
noPreserveState: false | ||
}; | ||
// eslint-disable-next-line no-undef | ||
const g = typeof window !== 'undefined' ? window : global; | ||
|
||
export function configure(options) { | ||
hotOptions = Object.assign(hotOptions, options); | ||
configureProxy(hotOptions); | ||
} | ||
const globalKey = | ||
typeof Symbol !== 'undefined' | ||
? Symbol('SVELTE_LOADER_HOT') | ||
: '__SVELTE_LOADER_HOT'; | ||
|
||
export function register(id, component) { | ||
if (!g[globalKey]) { | ||
// do updating refs counting to know when a full update has been applied | ||
let updatingCount = 0; | ||
|
||
//store original component in registry | ||
Registry.set(id, { | ||
rollback: null, | ||
component, | ||
instances: [] | ||
}); | ||
const notifyStart = () => { | ||
updatingCount++; | ||
}; | ||
|
||
//create the proxy itself | ||
const proxy = createProxy(id); | ||
const notifyError = reload => err => { | ||
const errString = (err && err.stack) || err; | ||
// eslint-disable-next-line no-console | ||
console.error( | ||
'[HMR] Failed to accept update (nollup compat mode)', | ||
errString | ||
); | ||
reload(); | ||
notifyEnd(); | ||
}; | ||
|
||
//patch the registry record with proxy constructor | ||
const record = Registry.get(id); | ||
record.proxy = proxy; | ||
Registry.set(id, record); | ||
const notifyEnd = () => { | ||
updatingCount--; | ||
if (updatingCount === 0) { | ||
// NOTE this message is important for timing in tests | ||
// eslint-disable-next-line no-console | ||
console.log('[HMR:Svelte] Up to date'); | ||
} | ||
}; | ||
|
||
return proxy; | ||
g[globalKey] = { | ||
hotStates: {}, | ||
notifyStart, | ||
notifyError, | ||
notifyEnd, | ||
}; | ||
} | ||
|
||
export function reload(id, component) { | ||
const runAcceptHandlers = acceptHandlers => { | ||
const queue = [...acceptHandlers]; | ||
const next = () => { | ||
const cur = queue.shift(); | ||
if (cur) { | ||
return cur(null).then(next); | ||
} else { | ||
return Promise.resolve(null); | ||
} | ||
}; | ||
return next(); | ||
}; | ||
|
||
const record = Registry.get(id); | ||
export const applyHmr = makeApplyHmr(args => { | ||
const { notifyStart, notifyError, notifyEnd } = g[globalKey]; | ||
const { m, reload } = args; | ||
|
||
//keep reference to previous version to enable rollback | ||
record.rollback = record.component; | ||
let acceptHandlers = (m.hot.data && m.hot.data.acceptHandlers) || []; | ||
let nextAcceptHandlers = []; | ||
|
||
//replace component in registry with newly loaded component | ||
record.component = component; | ||
m.hot.dispose(data => { | ||
data.acceptHandlers = nextAcceptHandlers; | ||
}); | ||
|
||
Registry.set(id, record); | ||
const dispose = (...args) => m.hot.dispose(...args); | ||
|
||
//re-render the proxy instances | ||
record.instances.slice().forEach(function(instance) { | ||
instance && instance._rerender(); | ||
const accept = handler => { | ||
if (nextAcceptHandlers.length === 0) { | ||
m.hot.accept(); | ||
} | ||
nextAcceptHandlers.push(handler); | ||
}; | ||
|
||
const check = status => { | ||
if (status === 'ready') { | ||
notifyStart(); | ||
} else if (status === 'idle') { | ||
runAcceptHandlers(acceptHandlers) | ||
.then(notifyEnd) | ||
.catch(notifyError(reload)); | ||
} | ||
}; | ||
|
||
m.hot.addStatusHandler(check); | ||
|
||
m.hot.dispose(() => { | ||
m.hot.removeStatusHandler(check); | ||
}); | ||
|
||
//return the original proxy constructor that was `register()`-ed | ||
return record.proxy; | ||
} | ||
const hot = { | ||
data: m.hot.data, | ||
dispose, | ||
accept, | ||
}; | ||
|
||
return Object.assign({}, args, { hot }); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
const { walk } = require('svelte/compiler'); | ||
const { createMakeHot } = require('svelte-hmr'); | ||
|
||
const hotApi = require.resolve('./hot-api.js'); | ||
|
||
const makeHot = createMakeHot({ | ||
walk, | ||
meta: 'module', | ||
hotApi, | ||
}); | ||
|
||
module.exports = makeHot; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -307,7 +307,7 @@ describe('loader', () => { | |
function(err, code, map) { | ||
expect(err).not.to.exist; | ||
|
||
expect(code).not.to.contain('module.hot.accept();'); | ||
expect(code).not.to.contain('module && module.hot'); | ||
}, | ||
{} | ||
) | ||
|
@@ -320,8 +320,8 @@ describe('loader', () => { | |
function(err, code, map) { | ||
expect(err).not.to.exist; | ||
|
||
expect(code).to.contain('module.hot.accept();'); | ||
expect(code).not.to.contain('configure({"noPreserveState":true});'); | ||
expect(code).to.contain('module && module.hot'); | ||
expect(code).not.to.contain('"noPreserveState":true'); | ||
}, | ||
{ hotReload: true } | ||
) | ||
|
@@ -334,8 +334,8 @@ describe('loader', () => { | |
function(err, code, map) { | ||
expect(err).not.to.exist; | ||
|
||
expect(code).to.contain('module.hot.accept();'); | ||
expect(code).to.contain('configure({"noPreserveState":true});'); | ||
expect(code).to.contain('module && module.hot'); | ||
expect(code).to.contain('"noPreserveState":true'); | ||
}, | ||
{ | ||
hotReload: true, | ||
|
@@ -353,7 +353,7 @@ describe('loader', () => { | |
function(err, code, map) { | ||
expect(err).not.to.exist; | ||
|
||
expect(code).not.to.contain('module.hot.accept();'); | ||
expect(code).not.to.contain('module && module.hot'); | ||
}, | ||
{ | ||
hotReload: true, | ||
|
@@ -369,7 +369,7 @@ describe('loader', () => { | |
function(err, code, map) { | ||
expect(err).not.to.exist; | ||
|
||
expect(code).to.contain(require.resolve('../lib/hot-api.js').replace(/[/\\]/g, '/')); | ||
expect(code).to.contain(`lib/hot-api.js`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use single quotes instead of backticks (if we apply svelte's linting to this repo at some point in the future that will be enforced. doing it now will make it easier to possibly setup) |
||
}, | ||
{ hotReload: true } | ||
) | ||
|
Uh oh!
There was an error while loading. Please reload this page.