@rsvelte/vite-plugin-svelte 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsvelte/vite-plugin-svelte",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "license": "MIT",
5
5
  "author": "dominikg",
6
6
  "files": [
@@ -34,12 +34,15 @@
34
34
  },
35
35
  "homepage": "https://github.com/baseballyama/rsvelte/tree/main/apps/npm/vite-plugin-svelte#readme",
36
36
  "dependencies": {
37
+ "@jridgewell/remapping": "^2.3.5",
38
+ "@jridgewell/trace-mapping": "^0.3.31",
37
39
  "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
40
+ "acorn": "^8.16.0",
38
41
  "deepmerge": "^4.3.1",
39
42
  "magic-string": "^1.0.0",
40
43
  "obug": "^2.1.0",
41
44
  "vitefu": "^1.1.1",
42
- "@rsvelte/vite-plugin-svelte-native": "^0.3.0"
45
+ "@rsvelte/vite-plugin-svelte-native": "^0.3.7"
43
46
  },
44
47
  "peerDependencies": {
45
48
  "svelte": "^5.0.0",
@@ -49,9 +52,11 @@
49
52
  "@types/debug": "^4.1.12",
50
53
  "sass": "^1.94.2",
51
54
  "svelte": "^5.56.4",
52
- "vite": "^8.0.0"
55
+ "vite": "^8.0.0",
56
+ "vitest": "^4.1.10"
53
57
  },
54
58
  "scripts": {
59
+ "test": "vitest run",
55
60
  "check:publint": "publint --strict",
56
61
  "check:types": "tsc --noEmit",
57
62
  "generate:types": "dts-buddy -m \"@rsvelte/vite-plugin-svelte:src/public.d.ts\""
@@ -1,8 +1,7 @@
1
1
  import * as svelte from '@rsvelte/vite-plugin-svelte-native';
2
2
  import { safeBase64Hash } from './hash.js';
3
3
  import { log } from './log.js';
4
-
5
- import { mapToRelative } from './sourcemaps.js';
4
+ import { postprocessCompiled } from './map-edits.js';
6
5
  import { enhanceCompileError } from './error.js';
7
6
 
8
7
  // TODO this is a patched version of https://github.com/sveltejs/vite-plugin-svelte/pull/796/files#diff-3bce0b33034aad4b35ca094893671f7e7ddf4d27254ae7b9b0f912027a001b15R10
@@ -104,18 +103,6 @@ export function createCompileSvelte() {
104
103
  typeof finalCompileOptions.cssHash === 'function'
105
104
  ? await svelte.compileAsync(finalCode, { ...finalCompileOptions, filename })
106
105
  : svelte.compile(finalCode, { ...finalCompileOptions, filename });
107
-
108
- // patch output with partial accept until svelte does it
109
- // TODO remove later
110
- if (
111
- options.server?.config.experimental.hmrPartialAccept &&
112
- compiled.js.code.includes('import.meta.hot.accept(')
113
- ) {
114
- compiled.js.code = compiled.js.code.replaceAll(
115
- 'import.meta.hot.accept(',
116
- 'import.meta.hot.acceptExports(["default"],'
117
- );
118
- }
119
106
  } catch (e) {
120
107
  enhanceCompileError(e, code, options.preprocess);
121
108
  throw e;
@@ -124,23 +111,21 @@ export function createCompileSvelte() {
124
111
  if (endStat) {
125
112
  endStat();
126
113
  }
127
- mapToRelative(compiled.js?.map, filename);
128
- mapToRelative(compiled.css?.map, filename);
129
114
  if (warnings.length) {
130
115
  if (!compiled.warnings) {
131
116
  compiled.warnings = [];
132
117
  }
133
118
  compiled.warnings.push(...warnings);
134
119
  }
135
- if (!raw) {
136
- // wire css import and code for hmr
137
- const hasCss = compiled.css?.code?.trim()?.length ?? 0 > 0;
138
- // compiler might not emit css with mode none or it may be empty
139
- if (emitCss && hasCss) {
140
- // TODO properly update sourcemap?
141
- compiled.js.code += `\nimport ${JSON.stringify(cssId)};\n`;
142
- }
143
- }
120
+ // compiler might not emit css with mode none or it may be empty
121
+ const hasCss = compiled.css?.code?.trim()?.length ?? 0 > 0;
122
+ postprocessCompiled(compiled, {
123
+ filename,
124
+ cssId,
125
+ // patch output with partial accept until svelte does it
126
+ partialAccept: !!options.server?.config.experimental.hmrPartialAccept,
127
+ emitCssImport: !raw && emitCss && hasCss
128
+ });
144
129
 
145
130
  let lang = 'js';
146
131
  for (const match of code.matchAll(scriptLangRE)) {
@@ -0,0 +1,108 @@
1
+ import remapping from '@jridgewell/remapping';
2
+ import { parse } from 'acorn';
3
+ import MagicString from 'magic-string';
4
+ import { mapToRelative } from './sourcemaps.js';
5
+
6
+ // remapping resolves a loaded map's sources against the URI of the source it
7
+ // replaces, so a name with a directory component would rewrite relative sources
8
+ // into absolute ones. A bare name leaves every spelling untouched.
9
+ const PRE_EDIT_SOURCE = 'rsvelte-pre-edit-source';
10
+
11
+ /** @param {any} callee */
12
+ function isHotAcceptCallee(callee) {
13
+ return (
14
+ callee.type === 'MemberExpression' &&
15
+ !callee.computed &&
16
+ callee.property.type === 'Identifier' &&
17
+ callee.property.name === 'accept' &&
18
+ callee.object.type === 'MemberExpression' &&
19
+ !callee.object.computed &&
20
+ callee.object.property.type === 'Identifier' &&
21
+ callee.object.property.name === 'hot' &&
22
+ callee.object.object.type === 'MetaProperty' &&
23
+ callee.object.object.meta.name === 'import' &&
24
+ callee.object.object.property.name === 'meta'
25
+ );
26
+ }
27
+
28
+ /**
29
+ * @param {any} node
30
+ * @param {any[]} calls
31
+ */
32
+ function collectHotAcceptCalls(node, calls) {
33
+ if (!node || typeof node !== 'object') return;
34
+ if (node.type === 'CallExpression' && isHotAcceptCallee(node.callee)) {
35
+ calls.push(node);
36
+ }
37
+ for (const value of Object.values(node)) {
38
+ if (Array.isArray(value)) {
39
+ for (const child of value) collectHotAcceptCalls(child, calls);
40
+ } else if (value && typeof value === 'object' && typeof value.type === 'string') {
41
+ collectHotAcceptCalls(value, calls);
42
+ }
43
+ }
44
+ }
45
+
46
+ /**
47
+ * @param {{ js: { code: string, map: any } }} compiled
48
+ * @param {(editor: MagicString, code: string) => void} edit
49
+ */
50
+ export function applyMapEdits(compiled, edit) {
51
+ const editor = new MagicString(compiled.js.code);
52
+ edit(editor, compiled.js.code);
53
+ const previousMap = compiled.js.map;
54
+ compiled.js.code = editor.toString();
55
+ if (!previousMap) return;
56
+ const editMap = editor.generateMap({ hires: true, source: PRE_EDIT_SOURCE });
57
+ let loadedPreviousMap = false;
58
+ const composed = remapping(/** @type {any} */ (editMap), (source) => {
59
+ if (source !== PRE_EDIT_SOURCE || loadedPreviousMap) return null;
60
+ loadedPreviousMap = true;
61
+ return /** @type {any} */ (previousMap);
62
+ });
63
+ // the edit map carries no `file`, so composing would otherwise drop it
64
+ if (previousMap.file != null) {
65
+ /** @type {any} */ (composed).file = previousMap.file;
66
+ }
67
+ compiled.js.map = composed;
68
+ }
69
+
70
+ /**
71
+ * Rewrite the compiled JavaScript for the dev server — HMR partial accept and the
72
+ * emitted-CSS import — keeping `js.map` in step with every edit.
73
+ *
74
+ * @param {{ js: { code: string, map: any }, css?: { map?: any } | null }} compiled
75
+ * @param {{ filename: string, cssId: string, partialAccept: boolean, emitCssImport: boolean }} options
76
+ */
77
+ export function postprocessCompiled(compiled, { filename, cssId, partialAccept, emitCssImport }) {
78
+ if (partialAccept && compiled.js.code.includes('import.meta.hot')) {
79
+ applyMapEdits(compiled, (editor, generated) => {
80
+ addPartialAcceptExports(editor, generated);
81
+ });
82
+ }
83
+ mapToRelative(compiled.js?.map, filename);
84
+ mapToRelative(compiled.css?.map, filename);
85
+ if (emitCssImport) {
86
+ applyMapEdits(compiled, (editor) => {
87
+ editor.append(`\nimport ${JSON.stringify(cssId)};\n`);
88
+ });
89
+ }
90
+ }
91
+
92
+ /**
93
+ * @param {MagicString} editor
94
+ * @param {string} code
95
+ */
96
+ export function addPartialAcceptExports(editor, code) {
97
+ const calls = [];
98
+ collectHotAcceptCalls(parse(code, { ecmaVersion: 'latest', sourceType: 'module' }), calls);
99
+ for (const call of calls) {
100
+ const firstArgument = call.arguments[0];
101
+ if (!firstArgument || firstArgument.type === 'SpreadElement') continue;
102
+ editor.overwrite(
103
+ call.callee.start,
104
+ firstArgument.start,
105
+ `${code.slice(call.callee.start, call.callee.end)}Exports([\"default\"],`
106
+ );
107
+ }
108
+ }