@sveltejs/vite-plugin-svelte 3.0.0-next.0 → 3.0.0-next.1

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": "@sveltejs/vite-plugin-svelte",
3
- "version": "3.0.0-next.0",
3
+ "version": "3.0.0-next.1",
4
4
  "license": "MIT",
5
5
  "author": "dominikg",
6
6
  "files": [
@@ -34,23 +34,23 @@
34
34
  },
35
35
  "homepage": "https://github.com/sveltejs/vite-plugin-svelte#readme",
36
36
  "dependencies": {
37
+ "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0-next.0 || ^2.0.0",
37
38
  "debug": "^4.3.4",
38
39
  "deepmerge": "^4.3.1",
39
40
  "kleur": "^4.1.5",
40
- "magic-string": "^0.30.3",
41
+ "magic-string": "^0.30.5",
41
42
  "svelte-hmr": "^0.15.3",
42
- "vitefu": "^0.2.4",
43
- "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0-next.0"
43
+ "vitefu": "^0.2.5"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "svelte": "^4.0.0",
47
47
  "vite": "^5.0.0-beta.1 || ^5.0.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@types/debug": "^4.1.8",
51
- "esbuild": "^0.19.3",
52
- "svelte": "^4.2.0",
53
- "vite": "^5.0.0-beta.1"
50
+ "@types/debug": "^4.1.9",
51
+ "esbuild": "^0.19.4",
52
+ "svelte": "^4.2.1",
53
+ "vite": "^5.0.0-beta.7"
54
54
  },
55
55
  "scripts": {
56
56
  "check:publint": "publint --strict",
package/src/index.d.ts CHANGED
@@ -121,20 +121,7 @@ interface SvelteOptions {
121
121
  * @see https://svelte.dev/docs#svelte_compile
122
122
  */
123
123
  compilerOptions?: Omit<CompileOptions, 'filename' | 'format' | 'generate'>;
124
- /**
125
- * Handles warning emitted from the Svelte compiler
126
- */
127
- onwarn?: (warning: Warning, defaultHandler?: (warning: Warning) => void) => void;
128
- /**
129
- * Options for vite-plugin-svelte
130
- */
131
- vitePlugin?: PluginOptions;
132
- }
133
124
 
134
- /**
135
- * These options are considered experimental and breaking changes to them can occur in any release
136
- */
137
- interface ExperimentalOptions {
138
125
  /**
139
126
  * A function to update `compilerOptions` before compilation
140
127
  *
@@ -159,6 +146,21 @@ interface ExperimentalOptions {
159
146
  code: string;
160
147
  compileOptions: Partial<CompileOptions>;
161
148
  }) => Promise<Partial<CompileOptions> | void> | Partial<CompileOptions> | void;
149
+
150
+ /**
151
+ * Handles warning emitted from the Svelte compiler
152
+ */
153
+ onwarn?: (warning: Warning, defaultHandler?: (warning: Warning) => void) => void;
154
+ /**
155
+ * Options for vite-plugin-svelte
156
+ */
157
+ vitePlugin?: PluginOptions;
158
+ }
159
+
160
+ /**
161
+ * These options are considered experimental and breaking changes to them can occur in any release
162
+ */
163
+ interface ExperimentalOptions {
162
164
  /**
163
165
  * send a websocket message with svelte compiler warnings during dev
164
166
  *
package/src/preprocess.js CHANGED
@@ -1,19 +1,18 @@
1
- import { preprocessCSS, resolveConfig, transformWithEsbuild } from 'vite';
1
+ import { isCSSRequest, preprocessCSS, resolveConfig, transformWithEsbuild } from 'vite';
2
2
  import { mapToRelative, removeLangSuffix } from './utils/sourcemaps.js';
3
3
 
4
4
  /**
5
5
  * @typedef {(code: string, filename: string) => Promise<{ code: string; map?: any; deps?: Set<string> }>} CssTransform
6
6
  */
7
7
 
8
- const supportedStyleLangs = ['css', 'less', 'sass', 'scss', 'styl', 'stylus', 'postcss', 'sss'];
9
8
  const supportedScriptLangs = ['ts'];
10
9
 
11
- export const lang_sep = '.vite-preprocess.';
10
+ export const lang_sep = '.vite-preprocess';
12
11
 
13
12
  /** @type {import('./index.d.ts').vitePreprocess} */
14
13
  export function vitePreprocess(opts) {
15
14
  /** @type {import('svelte/types/compiler/preprocess').PreprocessorGroup} */
16
- const preprocessor = {};
15
+ const preprocessor = { name: 'vite-preprocess' };
17
16
  if (opts?.script !== false) {
18
17
  preprocessor.script = viteScript().script;
19
18
  }
@@ -63,8 +62,8 @@ function viteStyle(config = {}) {
63
62
  let transform;
64
63
  /** @type {import('svelte/types/compiler/preprocess').Preprocessor} */
65
64
  const style = async ({ attributes, content, filename = '' }) => {
66
- const lang = /** @type {string} */ (attributes.lang);
67
- if (!supportedStyleLangs.includes(lang)) return;
65
+ const ext = attributes.lang ? `.${attributes.lang}` : '.css';
66
+ if (attributes.lang && !isCSSRequest(ext)) return;
68
67
  if (!transform) {
69
68
  /** @type {import('vite').ResolvedConfig} */
70
69
  let resolvedConfig;
@@ -82,7 +81,7 @@ function viteStyle(config = {}) {
82
81
  }
83
82
  transform = getCssTransformFn(resolvedConfig);
84
83
  }
85
- const suffix = `${lang_sep}${lang}`;
84
+ const suffix = `${lang_sep}${ext}`;
86
85
  const moduleId = `${filename}${suffix}`;
87
86
  const { code, map, deps } = await transform(content, moduleId);
88
87
  removeLangSuffix(map, suffix);
@@ -4,8 +4,12 @@ import { createMakeHot } from 'svelte-hmr';
4
4
  import { safeBase64Hash } from './hash.js';
5
5
  import { log } from './log.js';
6
6
 
7
- import { createInjectScopeEverythingRulePreprocessorGroup } from './preprocess.js';
7
+ import {
8
+ checkPreprocessDependencies,
9
+ createInjectScopeEverythingRulePreprocessorGroup
10
+ } from './preprocess.js';
8
11
  import { mapToRelative } from './sourcemaps.js';
12
+ import { enhanceCompileError } from './error.js';
9
13
 
10
14
  const scriptLangRE = /<script [^>]*lang=["']?([^"' >]+)["']?[^>]*>/;
11
15
 
@@ -21,7 +25,10 @@ export const _createCompileSvelte = (makeHot) => {
21
25
  return async function compileSvelte(svelteRequest, code, options) {
22
26
  const { filename, normalizedFilename, cssId, ssr, raw } = svelteRequest;
23
27
  const { emitCss = true } = options;
28
+ /** @type {string[]} */
24
29
  const dependencies = [];
30
+ /** @type {import('svelte/types/compiler/interfaces').Warning[]} */
31
+ const warnings = [];
25
32
 
26
33
  if (options.stats) {
27
34
  if (options.isBuild) {
@@ -86,7 +93,16 @@ export const _createCompileSvelte = (makeHot) => {
86
93
  throw e;
87
94
  }
88
95
 
89
- if (preprocessed.dependencies) dependencies.push(...preprocessed.dependencies);
96
+ if (preprocessed.dependencies?.length) {
97
+ const checked = checkPreprocessDependencies(filename, preprocessed.dependencies);
98
+ if (checked.warnings.length) {
99
+ warnings.push(...checked.warnings);
100
+ }
101
+ if (checked.dependencies.length) {
102
+ dependencies.push(...checked.dependencies);
103
+ }
104
+ }
105
+
90
106
  if (preprocessed.map) compileOptions.sourcemap = preprocessed.map;
91
107
  }
92
108
  if (typeof preprocessed?.map === 'object') {
@@ -99,7 +115,7 @@ export const _createCompileSvelte = (makeHot) => {
99
115
  };
100
116
  }
101
117
  const finalCode = preprocessed ? preprocessed.code : code;
102
- const dynamicCompileOptions = await options.experimental?.dynamicCompileOptions?.({
118
+ const dynamicCompileOptions = await options?.dynamicCompileOptions?.({
103
119
  filename,
104
120
  code: finalCode,
105
121
  compileOptions
@@ -119,13 +135,26 @@ export const _createCompileSvelte = (makeHot) => {
119
135
  : compileOptions;
120
136
 
121
137
  const endStat = stats?.start(filename);
122
- const compiled = compile(finalCode, finalCompileOptions);
138
+ /** @type {import('svelte/types/compiler/interfaces').CompileResult} */
139
+ let compiled;
140
+ try {
141
+ compiled = compile(finalCode, finalCompileOptions);
142
+ } catch (e) {
143
+ enhanceCompileError(e, code, preprocessors);
144
+ throw e;
145
+ }
123
146
 
124
147
  if (endStat) {
125
148
  endStat();
126
149
  }
127
150
  mapToRelative(compiled.js?.map, filename);
128
151
  mapToRelative(compiled.css?.map, filename);
152
+ if (warnings.length) {
153
+ if (!compiled.warnings) {
154
+ compiled.warnings = [];
155
+ }
156
+ compiled.warnings.push(...warnings);
157
+ }
129
158
  if (!raw) {
130
159
  // wire css import and code for hmr
131
160
  const hasCss = compiled.css?.code?.trim().length > 0;
@@ -100,3 +100,79 @@ function formatFrameForVite(frame) {
100
100
  .map((line) => (line.match(/^\s+\^/) ? ' ' + line : ' ' + line.replace(':', ' | ')))
101
101
  .join('\n');
102
102
  }
103
+
104
+ /**
105
+ * @param {import('svelte/types/compiler/interfaces').Warning & Error} err a svelte compiler error, which is a mix of Warning and an error
106
+ * @param {string} originalCode
107
+ * @param {import('../index.js').Arrayable<import('svelte/types/compiler/preprocess').PreprocessorGroup>} [preprocessors]
108
+ */
109
+ export function enhanceCompileError(err, originalCode, preprocessors) {
110
+ preprocessors = arraify(preprocessors ?? []);
111
+
112
+ /** @type {string[]} */
113
+ const additionalMessages = [];
114
+
115
+ // Handle incorrect TypeScript usage
116
+ if (err.code === 'parse-error') {
117
+ // Reference from Svelte: https://github.com/sveltejs/svelte/blob/800f6c076be5dd87dd4d2e9d66c59b973d54d84b/packages/svelte/src/compiler/preprocess/index.js#L262
118
+ const scriptRe = /<script(\s[^]*?)?(?:>([^]*?)<\/script>|\/>)/gi;
119
+ const errIndex = err.pos ?? -1;
120
+
121
+ let m;
122
+ while ((m = scriptRe.exec(originalCode))) {
123
+ const matchStart = m.index;
124
+ const matchEnd = matchStart + m[0].length;
125
+ const isErrorInScript = matchStart <= errIndex && errIndex <= matchEnd;
126
+ if (isErrorInScript) {
127
+ // Warn missing lang="ts"
128
+ const hasLangTs = m[1]?.includes('lang="ts"');
129
+ if (!hasLangTs) {
130
+ additionalMessages.push('Did you forget to add lang="ts" to your script tag?');
131
+ }
132
+ // Warn missing script preprocessor
133
+ if (preprocessors.every((p) => p.script == null)) {
134
+ const preprocessorType = hasLangTs ? 'TypeScript' : 'script';
135
+ additionalMessages.push(
136
+ `Did you forget to add a ${preprocessorType} preprocessor? See https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/preprocess.md for more information.`
137
+ );
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ // Handle incorrect CSS preprocessor usage
144
+ if (err.code === 'css-syntax-error') {
145
+ const styleRe = /<style(\s[^]*?)?(?:>([^]*?)<\/style>|\/>)/gi;
146
+
147
+ let m;
148
+ while ((m = styleRe.exec(originalCode))) {
149
+ // Warn missing lang attribute
150
+ if (!m[1]?.includes('lang=')) {
151
+ additionalMessages.push('Did you forget to add a lang attribute to your style tag?');
152
+ }
153
+ // Warn missing style preprocessor
154
+ if (
155
+ preprocessors.every((p) => p.style == null || p.name === 'inject-scope-everything-rule')
156
+ ) {
157
+ const preprocessorType = m[1]?.match(/lang="(.+?)"/)?.[1] ?? 'style';
158
+ additionalMessages.push(
159
+ `Did you forget to add a ${preprocessorType} preprocessor? See https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/preprocess.md for more information.`
160
+ );
161
+ }
162
+ }
163
+ }
164
+
165
+ if (additionalMessages.length) {
166
+ err.message += '\n\n- ' + additionalMessages.join('\n- ');
167
+ }
168
+
169
+ return err;
170
+ }
171
+
172
+ /**
173
+ * @param {T | T[]} value
174
+ * @template T
175
+ */
176
+ function arraify(value) {
177
+ return Array.isArray(value) ? value : [value];
178
+ }
@@ -81,14 +81,18 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
81
81
 
82
82
  const finalCode = preprocessed ? preprocessed.code : code;
83
83
 
84
- const dynamicCompileOptions = await options.experimental?.dynamicCompileOptions?.({
84
+ const dynamicCompileOptions = await options?.dynamicCompileOptions?.({
85
85
  filename,
86
86
  code: finalCode,
87
87
  compileOptions
88
88
  });
89
89
 
90
90
  if (dynamicCompileOptions && log.debug.enabled) {
91
- log.debug(`dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}`);
91
+ log.debug(
92
+ `dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}`,
93
+ undefined,
94
+ 'compile'
95
+ );
92
96
  }
93
97
 
94
98
  const finalCompileOptions = dynamicCompileOptions
@@ -59,7 +59,8 @@ export async function loadSvelteConfig(viteConfig, inlineOptions) {
59
59
  // identify which require function to use (esm and cjs mode)
60
60
  const _require = import.meta.url
61
61
  ? esmRequire ?? (esmRequire = createRequire(import.meta.url))
62
- : require;
62
+ : // eslint-disable-next-line no-undef
63
+ require;
63
64
 
64
65
  // avoid loading cached version on reload
65
66
  delete _require.cache[_require.resolve(configFile)];
@@ -36,6 +36,7 @@ const allowedPluginOptions = new Set([
36
36
  'disableDependencyReinclusion',
37
37
  'prebundleSvelteLibraries',
38
38
  'inspector',
39
+ 'dynamicCompileOptions',
39
40
  'experimental'
40
41
  ]);
41
42
 
@@ -316,13 +317,13 @@ function removeIgnoredOptions(options) {
316
317
  function handleDeprecatedOptions(options) {
317
318
  const experimental = /** @type {Record<string, any>} */ (options.experimental);
318
319
  if (experimental) {
319
- for (const promoted of ['prebundleSvelteLibraries', 'inspector']) {
320
+ for (const promoted of ['prebundleSvelteLibraries', 'inspector', 'dynamicCompileOptions']) {
320
321
  if (experimental[promoted]) {
321
322
  //@ts-expect-error untyped assign
322
323
  options[promoted] = experimental[promoted];
323
324
  delete experimental[promoted];
324
325
  log.warn(
325
- `Option "vitePlugin.experimental.${promoted}" is no longer experimental and has moved to "vitePlugin.${promoted}". Please update your svelte config.`
326
+ `Option "experimental.${promoted}" is no longer experimental and has moved to "${promoted}". Please update your Svelte or Vite config.`
326
327
  );
327
328
  }
328
329
  }
@@ -1,6 +1,7 @@
1
1
  import MagicString from 'magic-string';
2
2
  import { log } from './log.js';
3
3
  import path from 'node:path';
4
+ import { normalizePath } from 'vite';
4
5
 
5
6
  /**
6
7
  * this appends a *{} rule to component styles to force the svelte compiler to add style classes to all nodes
@@ -12,6 +13,7 @@ import path from 'node:path';
12
13
  */
13
14
  export function createInjectScopeEverythingRulePreprocessorGroup() {
14
15
  return {
16
+ name: 'inject-scope-everything-rule',
15
17
  style({ content, filename }) {
16
18
  const s = new MagicString(content);
17
19
  s.append(' *{}');
@@ -120,3 +122,52 @@ export function addExtraPreprocessors(options, config) {
120
122
  }
121
123
  }
122
124
  }
125
+
126
+ /**
127
+ *
128
+ * @param filename {string}
129
+ * @param dependencies {string[]}
130
+ * @returns {({dependencies: string[], warnings:import('svelte/types/compiler/interfaces').Warning[] })}
131
+ */
132
+ export function checkPreprocessDependencies(filename, dependencies) {
133
+ /** @type {import('svelte/types/compiler/interfaces').Warning[]} */
134
+ const warnings = [];
135
+
136
+ // to find self, we have to compare normalized filenames, but must keep the original values in `dependencies`
137
+ // because otherwise file watching on windows doesn't work
138
+ // so we track idx and filter by that in the end
139
+ /** @type {number[]} */
140
+ const selfIdx = [];
141
+ const normalizedFullFilename = normalizePath(filename);
142
+ const normalizedDeps = dependencies.map(normalizePath);
143
+ for (let i = 0; i < normalizedDeps.length; i++) {
144
+ if (normalizedDeps[i] === normalizedFullFilename) {
145
+ selfIdx.push(i);
146
+ }
147
+ }
148
+ const hasSelfDependency = selfIdx.length > 0;
149
+ if (hasSelfDependency) {
150
+ warnings.push({
151
+ code: 'vite-plugin-svelte-preprocess-depends-on-self',
152
+ message:
153
+ 'svelte.preprocess returned this file as a dependency of itself. This can be caused by an invalid configuration or importing generated code that depends on .svelte files (eg. tailwind base css)',
154
+ filename
155
+ });
156
+ }
157
+
158
+ if (dependencies.length > 10) {
159
+ warnings.push({
160
+ code: 'vite-plugin-svelte-preprocess-many-dependencies',
161
+ message: `svelte.preprocess depends on more than 10 external files which can cause slow builds and poor DX, try to reduce them. Found: ${dependencies.join(
162
+ ', '
163
+ )}`,
164
+ filename
165
+ });
166
+ }
167
+ return {
168
+ dependencies: hasSelfDependency
169
+ ? dependencies.filter((_, i) => !selfIdx.includes(i)) // remove self dependency
170
+ : dependencies,
171
+ warnings
172
+ };
173
+ }