Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

svelte-loader

Package Overview
Dependencies
Maintainers
3
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svelte-loader - npm Package Compare versions

Comparing version 2.13.6 to 3.0.0

lib/make-hot.js

13

CHANGELOG.md
# svelte-loader changelog
## 3.0.0
* Breaking: compiler options must now be specified under `compilerOptions` ([#159](https://github.com/sveltejs/svelte-loader/pull/159))
* Breaking: removed support for deprecated `style`, `script`, and `markup` options, which are now located under the `preprocess` configuration ([#158](https://github.com/sveltejs/svelte-loader/pull/158))
* Breaking: dropped Svelte 2 support ([#150](https://github.com/sveltejs/svelte-loader/pull/150))
* Breaking: dropped Node 8 support ([#157](https://github.com/sveltejs/svelte-loader/pull/157))
* Add Webpack 5 support ([#151](https://github.com/sveltejs/svelte-loader/pull/151))
* Webpack 5 requires [an additional rule](https://github.com/sveltejs/svelte-loader#usage) in `webpack.config.js` to load the Svelte runtime correctly
* Replace broken Svelte 2 HMR with the implementation from `rixo/svelte-loader-hot` ([#156](https://github.com/sveltejs/svelte-loader/pull/156))
* Add Node 14 support and remove virtual modules implementation (fixes [#125](https://github.com/sveltejs/svelte-loader/pull/125), [#131](https://github.com/sveltejs/svelte-loader/pull/131))
Thanks to @non25, @Smittyvb, @rixo, and the numerous others who contributed to this major release!
## 2.13.6

@@ -4,0 +17,0 @@

170

index.js

@@ -1,52 +0,6 @@

const { basename, extname, relative } = require('path');
const { relative } = require('path');
const { getOptions } = require('loader-utils');
const VirtualModules = require('./lib/virtual');
const { makeHot } = require('./lib/make-hot.js');
const { compile, preprocess } = require('svelte/compiler');
const hotApi = require.resolve('./lib/hot-api.js');
const { version } = require('svelte/package.json');
const major_version = +version[0];
const { compile, preprocess } = major_version >= 3
? require('svelte/compiler')
: require('svelte');
const pluginOptions = {
externalDependencies: true,
hotReload: true,
hotOptions: true,
preprocess: true,
emitCss: true,
// legacy
onwarn: true,
shared: true,
style: true,
script: true,
markup: true
};
function makeHot(id, code, hotOptions) {
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) {

@@ -56,60 +10,19 @@ return file.replace(/[/\\]/g, '/');

function sanitize(input) {
return basename(input)
.replace(extname(input), '')
.replace(/[^a-zA-Z_$0-9]+/g, '_')
.replace(/^_/, '')
.replace(/_$/, '')
.replace(/^(\d)/, '_$1');
}
const virtualModules = new Map();
let index = 0;
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1);
}
module.exports = function(source, map) {
this.cacheable();
function normalize(compiled) {
// svelte.compile signature changed in 1.60 — this avoids
// future deprecation warnings while preserving backwards
// compatibility
const js = compiled.js || { code: compiled.code, map: compiled.map };
const options = { ...getOptions(this) };
const callback = this.async();
const css = compiled.css && typeof compiled.css === 'object'
? compiled.css
: { code: compiled.css, map: compiled.cssMap };
return { js, css, ast: compiled.ast, warnings: compiled.warnings || compiled.stats.warnings || [] };
}
const warned = {};
function deprecatePreprocessOptions(options) {
const preprocessOptions = {};
['markup', 'style', 'script'].forEach(kind => {
if (options[kind]) {
if (!warned[kind]) {
console.warn(`[svelte-loader] DEPRECATION: options.${kind} is now options.preprocess.${kind}`);
warned[kind] = true;
}
preprocessOptions[kind] = options[kind];
}
});
options.preprocess = options.preprocess || preprocessOptions;
}
const virtualModuleInstances = new Map();
module.exports = function(source, map) {
if (this._compiler && !virtualModuleInstances.has(this._compiler)) {
virtualModuleInstances.set(this._compiler, new VirtualModules(this._compiler));
if (options.cssPath) {
const css = virtualModules.get(options.cssPath);
virtualModules.delete(options.cssPath);
callback(null, css);
return;
}
const virtualModules = virtualModuleInstances.get(this._compiler);
this.cacheable();
const options = Object.assign({}, getOptions(this));
const callback = this.async();
const isServer = this.target === 'node' || (options.generate && options.generate == 'ssr');
const isServer = this.target === 'node' || (options.compilerOptions && options.compilerOptions.generate == 'ssr');
const isProduction = this.minimize || process.env.NODE_ENV === 'production';

@@ -119,3 +32,5 @@

filename: this.resourcePath,
format: options.format || (major_version >= 3 ? 'esm' : 'es')
css: !options.emitCss,
...options.compilerOptions,
format: (options.compilerOptions && options.compilerOptions.format) || 'esm'
};

@@ -125,17 +40,3 @@

if (major_version >= 3) {
// TODO anything?
} else {
compileOptions.shared = options.shared || 'svelte/shared.js';
compileOptions.name = capitalize(sanitize(compileOptions.filename));
compileOptions.onwarn = options.onwarn || handleWarning;
}
for (const option in options) {
if (!pluginOptions[option]) compileOptions[option] = options[option];
}
if (options.emitCss) compileOptions.css = false;
deprecatePreprocessOptions(options);
options.preprocess = options.preprocess || {};
options.preprocess.filename = compileOptions.filename;

@@ -150,30 +51,23 @@

let { js, css, warnings } = normalize(compile(processed.toString(), compileOptions));
const compiled = compile(processed.toString(), compileOptions);
let { js, css, warnings } = compiled;
if (major_version >= 3) {
warnings.forEach(
options.onwarn
? warning => options.onwarn(warning, handleWarning)
: handleWarning
);
}
warnings.forEach(
options.onwarn
? warning => options.onwarn(warning, handleWarning)
: handleWarning
);
if (options.hotReload && !isProduction && !isServer) {
const hotOptions = Object.assign({}, options.hotOptions);
const hotOptions = { ...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) {
const cssFilepath = compileOptions.filename.replace(
/\.[^/.]+$/,
`.svelte.css`
);
const resource = posixify(compileOptions.filename);
const cssPath = `${resource}.${index++}.css`;
css.code += '\n/*# sourceMappingURL=' + css.map.toUrl() + '*/';
js.code = js.code + `\nimport '${posixify(cssFilepath)}';\n`;
if (virtualModules) {
virtualModules.writeModule(cssFilepath, css.code);
}
js.code += `\nimport '${cssPath}!=!svelte-loader?cssPath=${cssPath}!${resource}'\n;`;
virtualModules.set(cssPath, css.code);
}

@@ -180,0 +74,0 @@

@@ -1,51 +0,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 { ...args, hot };
});
{
"name": "svelte-loader",
"version": "2.13.6",
"version": "3.0.0",
"author": "Nico Rehwaldt <git_nikku@nixis.de>",

@@ -18,16 +18,17 @@ "description": "A webpack loader for svelte",

"dependencies": {
"loader-utils": "^1.1.0",
"svelte-dev-helper": "^1.1.9"
"loader-utils": "^2.0.0",
"svelte-dev-helper": "^1.1.9",
"svelte-hmr": "^0.12.3"
},
"devDependencies": {
"chai": "^4.1.2",
"eslint": "^5.4.0",
"eslint-plugin-mocha": "^5.2.0",
"mocha": "^5.2.0",
"sinon": "^6.1.5",
"sinon-chai": "^3.2.0",
"svelte": "^3.0.0-beta.5"
"chai": "^4.2.0",
"eslint": "^7.18.0",
"eslint-plugin-mocha": "^8.0.0",
"mocha": "^8.2.1",
"sinon": "^9.2.3",
"sinon-chai": "^3.5.0",
"svelte": "^3.0.0"
},
"peerDependencies": {
"svelte": ">1.44.0"
"svelte": ">3.0.0"
},

@@ -34,0 +35,0 @@ "repository": {

@@ -8,2 +8,9 @@ # svelte-loader

## Install
```
npm install --save svelte svelte-loader
```
## Usage

@@ -30,2 +37,9 @@

use: 'svelte-loader'
},
{
// required to prevent errors from Svelte on Webpack 5+, omit on Webpack 4
test: /node_modules\/svelte\/.*\.mjs$/,
resolve: {
fullySpecified: false
}
}

@@ -52,5 +66,8 @@ ...

A better option is to extract the CSS into a separate file. Using the `emitCss` option as shown below would cause a virtual CSS file to be emitted for each Svelte component. The resulting file is then imported by the component, thus following the standard Webpack compilation flow. Add [ExtractTextPlugin](https://github.com/webpack-contrib/extract-text-webpack-plugin) to the mix to output the css to a separate file.
A better option is to extract the CSS into a separate file. Using the `emitCss` option as shown below would cause a virtual CSS file to be emitted for each Svelte component. The resulting file is then imported by the component, thus following the standard Webpack compilation flow. Add [MiniCssExtractPlugin](https://github.com/webpack-contrib/mini-css-extract-plugin) to the mix to output the css to a separate file.
```javascript
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const mode = process.env.NODE_ENV || 'development';
const prod = mode === 'production';
...

@@ -72,6 +89,11 @@ module: {

test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader',
}),
use: [
prod ? MiniCssExtractPlugin.loader :'style-loader',
{
loader: 'css-loader',
options: {
url: false, //necessary if you use url('/path/to/some/asset.png|jpg|gif')
}
}
]
},

@@ -83,3 +105,3 @@ ...

plugins: [
new ExtractTextPlugin('styles.css'),
new MiniCssExtractPlugin('styles.css'),
...

@@ -90,4 +112,25 @@ ]

Alternatively, if you're handling styles in some other way and just want to prevent the CSS being added to your JavaScript bundle, use `css: false`.
Note that the configuration shown above switches off `MiniCssExtractPlugin` in development mode in favour of using CSS javascript injection. This is recommended by `MiniCssExtractPlugin` because it does not support hot reloading.
`prod` indicates, that `NODE_ENV=production` has been set from `package.json` or manually (`NODE_ENV=production npx webpack`) for production builds. We can rely on that to make dynamic adjustments to the config.
Additionally, if you're using multiple entrypoints, you may wish to change `new MiniCssExtractPlugin('styles.css')` for `new MiniCssExtractPlugin('[name].css')` to generate one CSS file per entrypoint.
Warning: in production, if you have set `sideEffects: false` in your `package.json`, `MiniCssExtractPlugin` has a tendency to drop CSS, regardless of whether it's included in your svelte components.
Alternatively, if you're handling styles in some other way and just want to prevent the CSS being added to your JavaScript bundle, use
```javascript
...
use: {
loader: 'svelte-loader',
options: {
compilerOptions: {
css: false
}
},
},
...
```
### Source maps

@@ -108,17 +151,12 @@

{
test: /\.(html|svelte)$/,
exclude: /node_modules/,
use: {
loader: 'svelte-loader',
options: {
emitCss: true,
},
},
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{ loader: 'css-loader', options: { sourceMap: true } }],
}),
use: [
prod ? MiniCssExtractPlugin.loader :'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},

@@ -130,3 +168,3 @@ ...

plugins: [
new ExtractTextPlugin('styles.css'),
new MiniCssExtractPlugin('styles.css'),
...

@@ -140,7 +178,32 @@ ]

### Svelte Compiler options
You can specify additional arbitrary compilation options with the `compilerOptions` config key, which are passed directly to the underlying Svelte compiler:
```js
...
use: {
loader: 'svelte-loader',
options: {
compilerOptions: {
// additional compiler options here
generate: 'ssr', // for example, SSR can be enabled here
}
},
},
...
```
### Hot Reload
Hot reloading is turned off by default, you can turn it on using the `hotReload` option as shown below:
This loader supports component-level HMR via the community supported [svelte-hmr](https://github.com/rixo/svelte-hmr) package. This package serves as a testbed and early access for Svelte HMR, while we figure out how to best include HMR support in the compiler itself (which is tricky to do without unfairly favoring any particular dev tooling). Feedback, suggestion, or help to move HMR forward is welcomed at [svelte-hmr](https://github.com/rixo/svelte-hmr/issues) (for now).
Configure inside your `webpack.config.js`:
```javascript
// It is recommended to adjust svelte options dynamically, by using
// environment variables
const mode = process.env.NODE_ENV || 'development';
const prod = mode === 'production';
module.exports = {
...

@@ -156,86 +219,69 @@ module: {

options: {
hotReload: true
}
}
}
...
]
}
...
```
compilerOptions: {
// NOTE Svelte's dev mode MUST be enabled for HMR to work
dev: !prod, // Default: false
},
#### Hot reload rules and caveats:
// NOTE emitCss: true is currently not supported with HMR
// Enable it for production to output separate css file
emitCss: prod, // Default: false
// Enable HMR only for dev mode
hotReload: !prod, // Default: false
// Extra HMR options, the defaults are completely fine
// You can safely omit hotOptions altogether
hotOptions: {
// Prevent preserving local component state
preserveLocalState: false,
- `_rerender` and `_register` are reserved method names, please don't use them in `methods:{...}`
- Turning `dev` mode on (`dev:true`) is **not** necessary.
- Modifying the HTML (template) part of your component will replace and re-render the changes in place. Current local state of the component will also be preserved (this can be turned off per component see [Stop preserving state](#stop-preserving-state)).
- When modifying the `<script>` part of your component, instances will be replaced and re-rendered in place too.
However if your component has lifecycle methods that produce global side-effects, you might need to reload the whole page.
- If you are using `svelte/store`, a full reload is required if you modify `store` properties
// If this string appears anywhere in your component's code, then local
// state won't be preserved, even when noPreserveState is false
noPreserveStateKey: '@!hmr',
// Prevent doing a full reload on next HMR update after fatal error
noReload: false,
Components will **not** be hot reloaded in the following situations:
1. `process.env.NODE_ENV === 'production'`
2. Webpack is minifying code
3. Webpack's `target` is `node` (i.e SSR components)
4. `generate` option has a value of `ssr`
// Try to recover after runtime errors in component init
optimistic: false,
#### Stop preserving state
// --- Advanced ---
Sometimes it might be necessary for some components to avoid state preservation on hot reload.
This can be configured on a per-component basis by adding a property `noPreserveState = true` to the component's constructor using the `setup()` method. For example:
```js
export default {
setup(comp){
comp.noPreserveState = true;
},
data(){return {...}},
oncreate(){...}
}
```
Or, on a global basis by adding `{noPreserveState: true}` to `hotOptions`. For example:
```js
{
test: /\.(html|svelte)$/,
exclude: /node_modules/,
use: [
{
loader: 'svelte-loader',
options: {
hotReload: true,
hotOptions: {
noPreserveState: true
// Prevent adding an HMR accept handler to components with
// accessors option to true, or to components with named exports
// (from <script context="module">). This have the effect of
// recreating the consumer of those components, instead of the
// component themselves, on HMR updates. This might be needed to
// reflect changes to accessors / named exports in the parents,
// depending on how you use them.
acceptAccessors: true,
acceptNamedExports: true,
}
}
}
}
...
]
}
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
...
]
}
```
**Please Note:** If you are using `svelte/store`, `noPreserveState` has no effect on `store` properties. Neither locally, nor globally.
You also need to add the [HotModuleReplacementPlugin](https://webpack.js.org/plugins/hot-module-replacement-plugin/). There are multiple ways to achieve this.
#### External Dependencies
If you're using webpack-dev-server, you can just pass it the [`hot` option](https://webpack.js.org/configuration/dev-server/#devserverhot) to add the plugin automatically.
If you rely on any external dependencies (files required in a preprocessor for example) you might want to watch these files for changes and re-run svelte compile.
Otherwise, you can add it to your webpack config directly:
Webpack allows [loader dependencies](https://webpack.js.org/contribute/writing-a-loader/#loader-dependencies) to trigger a recompile. svelte-loader exposes this API via `options.externalDependencies`.
For example:
```js
const webpack = require('webpack');
```js
...
const variables = path.resolve('./variables.js');
...
{
test: /\.(html|svelte)$/,
use: [
{
loader: 'svelte-loader',
options: {
externalDependencies: [variables]
}
}
]
}
module.exports = {
...
plugins: [
new webpack.HotModuleReplacementPlugin(),
...
]
}
```

@@ -242,0 +288,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc