Skip to content

Commit e5d4749

Browse files
authored
fix: watch preprocessor dependencies (#34) (fixes #25)
* wip: watch preprocessor dependencies * wip: add tests for dependency watching * wip: add tests for dependency watching * fix: watch preprocessor dependencies and trigger hmr update on change (fixes #25) * obey linter * chore: update dependencies
1 parent 4018ce6 commit e5d4749

29 files changed

+492
-28
lines changed

.changeset/nasty-baboons-joke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/vite-plugin-svelte': patch
3+
---
4+
5+
fix: watch preprocessor dependencies and trigger hmr on change
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.vscode
2+
.idea
3+
node_modules
4+
dist
5+
dist-ssr
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Svelte + TS + Vite
2+
3+
This template should help get you started developing with Svelte and TypeScript in Vite.
4+
5+
## Recommended IDE Setup
6+
7+
[VSCode](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
8+
9+
## Need an official Svelte framework?
10+
11+
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
12+
13+
## Technical considerations
14+
15+
**Why use this over SvelteKit?**
16+
17+
- SvelteKit is still a work-in-progress.
18+
- It currently does not support the pure-SPA use case.
19+
- It brings its own routing solution which might not be preferable for some users.
20+
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
21+
`vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example.
22+
23+
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-app` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
24+
25+
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
26+
27+
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
28+
29+
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
30+
31+
**Why include `.vscode/extensions.json`?**
32+
33+
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
34+
35+
**Why enable `allowJs` in the TS template?**
36+
37+
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
38+
39+
**Why is HMR not preserving my local component state?**
40+
41+
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
42+
43+
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
44+
45+
```ts
46+
// store.ts
47+
// An extremely simple external store
48+
import { writable } from 'svelte/store';
49+
export default writable(0);
50+
```
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import {
2+
isBuild,
3+
getEl,
4+
getText,
5+
editFileAndWaitForHmrComplete,
6+
untilUpdated,
7+
sleep,
8+
getColor,
9+
addFile,
10+
removeFile
11+
} from '../../testUtils';
12+
13+
test('should render App', async () => {
14+
expect(await getText('h1')).toBe(`I'm blue`);
15+
expect(await getColor('h1')).toBe('blue');
16+
expect(await getText('h2')).toBe(`I'm red`);
17+
expect(await getColor('h2')).toBe('red');
18+
expect(await getText('p')).toBe(`I'm green`);
19+
expect(await getColor('p')).toBe('green');
20+
expect(await getText('span')).toBe(`I'm orangered`);
21+
expect(await getColor('span')).toBe('orangered');
22+
});
23+
24+
test('should not have failed requests', async () => {
25+
browserLogs.forEach((msg) => {
26+
expect(msg).not.toMatch('404');
27+
});
28+
});
29+
30+
if (!isBuild) {
31+
describe('hmr', () => {
32+
test('should apply updates when editing App.svelte', async () => {
33+
expect(await getText('span')).toBe(`I'm orangered`);
34+
await editFileAndWaitForHmrComplete('src/App.svelte', (c) =>
35+
c.replace(`I'm orangered`, `I'm replaced`)
36+
);
37+
expect(await getText('span')).toBe(`I'm replaced`);
38+
expect(await getColor('span')).toBe('orangered');
39+
await editFileAndWaitForHmrComplete(
40+
'src/App.svelte',
41+
(c) => c.replace(`color: orangered`, `color: magenta`),
42+
'/src/App.svelte?svelte&type=style&lang.css'
43+
);
44+
expect(await getColor('span')).toBe('magenta');
45+
});
46+
47+
test('should apply updates when editing MultiFile.html', async () => {
48+
expect(await getText('h1')).toBe(`I'm blue`);
49+
expect(await getText('h2')).toBe(`I'm red`);
50+
await editFileAndWaitForHmrComplete(
51+
'src/lib/multifile/MultiFile.html',
52+
(c) => c.replace(`I'm blue`, `I'm replaced`).replace(`I'm red`, `I'm replaced too`),
53+
'/src/lib/multifile/MultiFile.svelte'
54+
);
55+
expect(await getText('h1')).toBe(`I'm replaced`);
56+
expect(await getText('h2')).toBe(`I'm replaced too`);
57+
});
58+
59+
test('should apply updates when editing MultiFile.scss', async () => {
60+
expect(await getColor('h1')).toBe('blue');
61+
await editFileAndWaitForHmrComplete(
62+
'src/lib/multifile/MultiFile.scss',
63+
(c) => c.replace(`color: blue`, `color: magenta`),
64+
'/src/lib/multifile/MultiFile.svelte?svelte&type=style&lang.css'
65+
);
66+
expect(await getColor('h1')).toBe('magenta');
67+
});
68+
69+
test('should apply updates when editing _someImport.scss', async () => {
70+
expect(await getColor('h2')).toBe('red');
71+
await editFileAndWaitForHmrComplete(
72+
'src/lib/multifile/_someImport.scss',
73+
(c) => c.replace(`color: red`, `color: magenta`),
74+
'/src/lib/multifile/MultiFile.svelte?svelte&type=style&lang.css'
75+
);
76+
expect(await getColor('h2')).toBe('magenta');
77+
});
78+
79+
test('should remove styles that are no longer imported', async () => {
80+
expect(await getColor('h2')).toBe('magenta');
81+
await editFileAndWaitForHmrComplete(
82+
'src/lib/multifile/MultiFile.scss',
83+
(c) => c.replace(`@import 'someImport';`, `/*@import 'someImport';*/`),
84+
'/src/lib/multifile/MultiFile.svelte?svelte&type=style&lang.css'
85+
);
86+
expect(await getColor('h2')).toBe('black');
87+
});
88+
89+
test('should apply styles from new dependency', async () => {
90+
expect(await getColor('h2')).toBe('black');
91+
await addFile('src/lib/multifile/_foo.scss', 'h2 { color: maroon; }');
92+
expect(await getColor('h2')).toBe('black');
93+
await editFileAndWaitForHmrComplete(
94+
'src/lib/multifile/MultiFile.scss',
95+
(c) => c.replace(`/*@import 'someImport';*/`, `/*@import 'someImport';*/\n@import 'foo';`),
96+
'/src/lib/multifile/MultiFile.svelte?svelte&type=style&lang.css'
97+
);
98+
expect(await getColor('h2')).toBe('maroon');
99+
await editFileAndWaitForHmrComplete(
100+
'src/lib/multifile/_foo.scss',
101+
(c) => c.replace(`maroon`, `green`),
102+
'/src/lib/multifile/MultiFile.svelte?svelte&type=style&lang.css'
103+
);
104+
expect(await getColor('h2')).toBe('green');
105+
});
106+
107+
test('should apply updates when editing MultiFile.ts', async () => {
108+
expect(await getText('p')).toBe(`I'm green`);
109+
await editFileAndWaitForHmrComplete(
110+
'src/lib/multifile/MultiFile.ts',
111+
(c) => c.replace(`'green'`, `'a replaced value'`),
112+
'/src/lib/multifile/MultiFile.svelte'
113+
);
114+
expect(await getText('p')).toBe(`I'm a replaced value`);
115+
});
116+
117+
test('should apply updates when editing someother.css', async () => {
118+
expect(await getColor('p')).toBe('green');
119+
await editFileAndWaitForHmrComplete('src/lib/multifile/someother.css', (c) =>
120+
c.replace(`color: green`, `color: magenta`)
121+
);
122+
expect(await getColor('p')).toBe('magenta');
123+
});
124+
125+
test('should show error on deleting dependency', async () => {
126+
expect(await getColor('h2')).toBe('green');
127+
await removeFile('src/lib/multifile/_foo.scss');
128+
expect(await getColor('h2')).toBe('green');
129+
const errorOverlay = await page.waitForSelector('vite-error-overlay');
130+
expect(errorOverlay).toBeTruthy();
131+
await errorOverlay.click({ position: { x: 1, y: 1 } });
132+
await sleep(50);
133+
const errorOverlay2 = await getEl('vite-error-overlay');
134+
expect(errorOverlay2).toBeFalsy();
135+
await editFileAndWaitForHmrComplete(
136+
'src/lib/multifile/MultiFile.scss',
137+
(c) => c.replace(`@import 'foo';`, ``),
138+
'/src/lib/multifile/MultiFile.svelte?svelte&type=style&lang.css'
139+
);
140+
expect(await getColor('h2')).toBe('black');
141+
});
142+
});
143+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" href="/favicon.ico" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Svelte + TS + Vite App</title>
8+
</head>
9+
<body>
10+
<div id="app"></div>
11+
<script type="module" src="/src/main.ts"></script>
12+
</body>
13+
</html>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "playground-svelte-preprocess",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"serve": "vite preview"
9+
},
10+
"devDependencies": {
11+
"@sveltejs/vite-plugin-svelte": "workspace:*",
12+
"svelte": "^3.37.0",
13+
"svelte-preprocess": "^4.7.2",
14+
"typescript": "^4.2.4",
15+
"vite": "^2.2.3"
16+
}
17+
}
Binary file not shown.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script lang="ts">
2+
import logo from './assets/svelte.png';
3+
import MultiFile from './lib/multifile/MultiFile.svelte';
4+
</script>
5+
6+
<main>
7+
<img src={logo} alt="Svelte Logo" />
8+
<MultiFile />
9+
<span>I'm orangered</span>
10+
</main>
11+
12+
<style>
13+
span {
14+
color: orangered;
15+
}
16+
</style>
5.06 KB
Loading
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/// <reference types="svelte" />
2+
/// <reference types="vite/client" />

0 commit comments

Comments
 (0)