diff --git a/plugins/plugin-svelte/README.md b/plugins/plugin-svelte/README.md index 8488f0f0d3..d55cedaf6f 100644 --- a/plugins/plugin-svelte/README.md +++ b/plugins/plugin-svelte/README.md @@ -15,6 +15,31 @@ npm install --save-dev @snowpack/plugin-svelte } ``` -#### Plugin Options +## Plugin Options This plugin also supports all Svelte compiler options. See [here](https://svelte.dev/docs#svelte_compile) for a list of supported options. + +### HMR Options + +You can pass Svelte HMR specific options through the `hot` option of the plugin. Here are the available options and their defaults: + +```js +{ + "plugins": [ + ["@snowpack/plugin-svelte", { + hot: { + // don't preserve local state + noPreserveState: false, + // escape hatch from preserve local state -- if this string appears anywhere + // in the component's code, then state won't be preserved for this update + noPreserveStateKey: '@!hmr', + // don't reload on fatal error + noReload: false, + // try to recover after runtime errors during component init + optimistic: false, + }, + }] + ] +} +``` + diff --git a/plugins/plugin-svelte/package.json b/plugins/plugin-svelte/package.json index fbdf1bddaf..6a86eb2d67 100644 --- a/plugins/plugin-svelte/package.json +++ b/plugins/plugin-svelte/package.json @@ -17,7 +17,8 @@ }, "gitHead": "a01616bb0787d56cd782f94cecf2daa12c7594e4", "dependencies": { - "rollup-plugin-svelte": "^5.2.3" + "rollup-plugin-svelte": "^5.2.3", + "svelte-hmr": "^0.11.2-1" }, "devDependencies": { "node-sass": "^4.14.1", diff --git a/plugins/plugin-svelte/plugin.js b/plugins/plugin-svelte/plugin.js index 5ced6a476a..fbcb532584 100644 --- a/plugins/plugin-svelte/plugin.js +++ b/plugins/plugin-svelte/plugin.js @@ -2,8 +2,14 @@ const svelte = require('svelte/compiler'); const svelteRollupPlugin = require('rollup-plugin-svelte'); const fs = require('fs'); const path = require('path'); +const {createMakeHot} = require('svelte-hmr'); -module.exports = function plugin(snowpackConfig, pluginOptions = {}) { +let makeHot = (...args) => { + makeHot = createMakeHot({walk: svelte.walk}); + return makeHot(...args) +} + +module.exports = function plugin(snowpackConfig, {hot: hotOptions, ...sveltePluginOptions} = {}) { const isDev = process.env.NODE_ENV !== 'production'; // Support importing Svelte files when you install dependencies. @@ -15,7 +21,7 @@ module.exports = function plugin(snowpackConfig, pluginOptions = {}) { let preprocessOptions; // Note(drew): __config is for internal testing use; maybe we should make this public at some point? const userSvelteConfigLoc = - pluginOptions.__config || path.join(process.cwd(), 'svelte.config.js'); + sveltePluginOptions.__config || path.join(process.cwd(), 'svelte.config.js'); if (fs.existsSync(userSvelteConfigLoc)) { const userSvelteConfig = require(userSvelteConfigLoc); const {preprocess, ..._svelteOptions} = userSvelteConfig; @@ -27,7 +33,7 @@ module.exports = function plugin(snowpackConfig, pluginOptions = {}) { dev: isDev, css: false, ...svelteOptions, - ...pluginOptions, + ...sveltePluginOptions, }; return { @@ -36,9 +42,13 @@ module.exports = function plugin(snowpackConfig, pluginOptions = {}) { input: ['.svelte'], output: ['.js', '.css'], }, - knownEntrypoints: ['svelte/internal'], - async load({filePath, isSSR}) { - let codeToCompile = fs.readFileSync(filePath, 'utf-8'); + knownEntrypoints: [ + 'svelte/internal', + 'svelte-hmr/runtime/hot-api-esm.js', + 'svelte-hmr/runtime/proxy-adapter-dom.js', + ], + async load({filePath, isHmrEnabled, isSSR}) { + let codeToCompile = await fs.promises.readFile(filePath, 'utf-8'); // PRE-PROCESS if (preprocessOptions) { codeToCompile = ( @@ -48,12 +58,16 @@ module.exports = function plugin(snowpackConfig, pluginOptions = {}) { ).code; } - const {js, css} = svelte.compile(codeToCompile, { + const compileOptions = { generate: isSSR ? 'ssr' : 'dom', ...svelteOptions, // Note(drew) should take precedence over generate above outputFilename: filePath, filename: filePath, - }); + }; + + const compiled = svelte.compile(codeToCompile, compileOptions); + + const {js, css} = compiled; const {sourceMaps} = snowpackConfig.buildOptions; const output = { @@ -62,6 +76,22 @@ module.exports = function plugin(snowpackConfig, pluginOptions = {}) { map: sourceMaps ? js.map : undefined, }, }; + + if (isHmrEnabled && !isSSR) { + output['.js'].code = makeHot({ + id: filePath, + compiledCode: compiled.js.code, + hotOptions: { + ...hotOptions, + absoluteImports: false, + injectCss: true, + }, + compiled, + originalCode: codeToCompile, + compileOptions, + }); + } + if (!svelteOptions.css && css && css.code) { output['.css'] = { code: css.code, diff --git a/yarn.lock b/yarn.lock index c3b93776f0..5ced299c0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13060,6 +13060,11 @@ svelte-check@^1.0.0: vscode-languageserver-types "3.15.1" vscode-uri "2.1.2" +svelte-hmr@^0.11.2-1: + version "0.11.2-1" + resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.11.2-1.tgz#ce1b481a2489697ccf7aa405bccae1421ce80011" + integrity sha512-VanuFg1OiMvLjs44jo8+qnZnKfQGfflNRf8hETUhOs28J5V4xsCgU2NJHy+3iTVgEX1+a41zIEIBgquh02o9bQ== + svelte-language-server@*: version "0.10.133" resolved "https://registry.yarnpkg.com/svelte-language-server/-/svelte-language-server-0.10.133.tgz#e5d121a1a27090076e6c6c046b1aeeb45b5d0b93"