Skip to content

Commit

Permalink
Chore/v2 cleanup (#782)
Browse files Browse the repository at this point in the history
* feat: add experimental support for Svelte 5

* add experimental note
  • Loading branch information
dummdidumm authored Nov 9, 2023
1 parent 907e810 commit 31ce52c
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/poor-avocados-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/vite-plugin-svelte': minor
---

feat: add experimental support for Svelte 5
2 changes: 1 addition & 1 deletion packages/vite-plugin-svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"vitefu": "^0.2.4"
},
"peerDependencies": {
"svelte": "^3.54.0 || ^4.0.0",
"svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0",
"vite": "^4.0.0"
},
"devDependencies": {
Expand Down
10 changes: 10 additions & 0 deletions packages/vite-plugin-svelte/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ interface ExperimentalOptions {
* @default false
*/
disableSvelteResolveWarnings?: boolean;
/**
* Options for compiling Svelte JS/TS modules
*/
compileModule?: CompileModuleOptions;
}

interface CompileModuleOptions {
extensions?: string[];
include?: Arrayable<string>;
exclude?: Arrayable<string>;
}

type ModuleFormat = NonNullable<'esm'>;
Expand Down
46 changes: 42 additions & 4 deletions packages/vite-plugin-svelte/src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import fs from 'node:fs';
import { version as viteVersion } from 'vite';
import * as svelteCompiler from 'svelte/compiler';

import { svelteInspector } from '@sveltejs/vite-plugin-svelte-inspector';

import { isDepExcluded } from 'vitefu';
import { handleHotUpdate } from './handle-hot-update.js';
import { log, logCompilerWarnings } from './utils/log.js';
import { createCompileSvelte } from './utils/compile.js';
import { buildIdParser } from './utils/id.js';
import { buildIdParser, buildModuleIdParser } from './utils/id.js';
import {
buildExtraViteConfig,
validateInlineOptions,
Expand All @@ -24,7 +25,7 @@ import { saveSvelteMetadata } from './utils/optimizer.js';
import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache.js';
import { loadRaw } from './utils/load-raw.js';
import { FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE } from './utils/constants.js';
import { isSvelte3 } from './utils/svelte-version.js';
import { isSvelte3, isSvelte5 } from './utils/svelte-version.js';

const isVite4_0 = viteVersion.startsWith('4.0');

Expand All @@ -38,6 +39,8 @@ export function svelte(inlineOptions) {
// updated in configResolved hook
/** @type {import('./types/id.d.ts').IdParser} */
let requestParser;
/** @type {import('./types/id.d.ts').ModuleIdParser} */
let moduleRequestParser;
/** @type {import('./types/options.d.ts').ResolvedOptions} */
let options;
/** @type {import('vite').ResolvedConfig} */
Expand Down Expand Up @@ -268,9 +271,44 @@ export function svelte(inlineOptions) {
);
}
}
},
svelteInspector()
}
];

if (!isSvelte5) {
plugins.push(svelteInspector()); // TODO reenable once svelte5 has support
}
if (isSvelte5) {
log.warn(
'svelte 5 support in v-p-s is experimental, breaking changes can occur in any release until this notice is removed'
);
log.warn('svelte 5 does not support svelte-inspector yet, disabling it');
// TODO move to separate file
plugins.push({
name: 'vite-plugin-svelte-module',
enforce: 'post',
async configResolved() {
moduleRequestParser = buildModuleIdParser(options);
},
async transform(code, id, opts) {
const ssr = !!opts?.ssr;
const moduleRequest = moduleRequestParser(id, ssr);
if (!moduleRequest) {
return;
}
try {
const compileResult = await svelteCompiler.compileModule(code, {
generate: ssr ? 'ssr' : 'dom',
filename: moduleRequest.filename
});
logCompilerWarnings(moduleRequest, compileResult.warnings, options);
return compileResult.js;
} catch (e) {
throw toRollupError(e, options);
}
}
});
}

return plugins;
}

Expand Down
15 changes: 15 additions & 0 deletions packages/vite-plugin-svelte/src/types/id.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,19 @@ export interface SvelteRequest {
raw: boolean;
}

export interface SvelteModuleRequest {
id: string;
filename: string;
normalizedFilename: string;
query: RequestQuery;
timestamp: number;
ssr: boolean;
}

export type IdParser = (id: string, ssr: boolean, timestamp?: number) => SvelteRequest | undefined;

export type ModuleIdParser = (
id: string,
ssr: boolean,
timestamp?: number
) => SvelteModuleRequest | undefined;
9 changes: 5 additions & 4 deletions packages/vite-plugin-svelte/src/utils/compile.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { compile, preprocess, walk } from 'svelte/compiler';
import * as svelte from 'svelte/compiler';
// @ts-ignore
import { createMakeHot } from 'svelte-hmr';
import { safeBase64Hash } from './hash.js';
Expand Down Expand Up @@ -86,7 +86,7 @@ export const _createCompileSvelte = (makeHot) => {
}
if (preprocessors) {
try {
preprocessed = await preprocess(code, preprocessors, { filename }); // full filename here so postcss works
preprocessed = await svelte.preprocess(code, preprocessors, { filename }); // full filename here so postcss works
} catch (e) {
e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
throw e;
Expand Down Expand Up @@ -123,7 +123,7 @@ export const _createCompileSvelte = (makeHot) => {
: compileOptions;

const endStat = stats?.start(filename);
const compiled = compile(finalCode, finalCompileOptions);
const compiled = svelte.compile(finalCode, finalCompileOptions);

if (isSvelte3) {
// prevent dangling pure comments
Expand Down Expand Up @@ -187,7 +187,8 @@ function buildMakeHot(options) {
// @ts-ignore
const adapter = options?.hot?.adapter;
return createMakeHot({
walk,
// TODO Svelte 5 doesn't expose walk anymore. If we decide to make v-p-s 2 work with Svelte 5 HMR, we need to import walk from estree-walker
walk: svelte.walk,
hotApi,
adapter,
hotOptions: { noOverlay: true, .../** @type {object} */ (options.hot) }
Expand Down
28 changes: 24 additions & 4 deletions packages/vite-plugin-svelte/src/utils/esbuild.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { readFileSync } from 'node:fs';
import { compile, preprocess } from 'svelte/compiler';
import * as svelte from 'svelte/compiler';
import { log } from './log.js';
import { toESBuildError } from './error.js';
import { isSvelte3 } from './svelte-version.js';
import { isSvelte3, isSvelte5 } from './svelte-version.js';

/**
* @typedef {NonNullable<import('vite').DepOptimizationOptions['esbuildOptions']>} EsbuildOptions
Expand All @@ -11,6 +11,8 @@ import { isSvelte3 } from './svelte-version.js';

export const facadeEsbuildSveltePluginName = 'vite-plugin-svelte:facade';

const svelteModuleExtension = '.svelte.js';

/**
* @param {import('../types/options.d.ts').ResolvedOptions} options
* @returns {EsbuildPlugin}
Expand All @@ -24,6 +26,9 @@ export function esbuildSveltePlugin(options) {
if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return;

const svelteExtensions = (options.extensions ?? ['.svelte']).map((ext) => ext.slice(1));
if (isSvelte5) {
svelteExtensions.push(svelteModuleExtension.slice(1));
}
const svelteFilter = new RegExp('\\.(' + svelteExtensions.join('|') + ')(\\?.*)?$');
/** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */
let statsCollection;
Expand Down Expand Up @@ -55,6 +60,21 @@ export function esbuildSveltePlugin(options) {
* @returns {Promise<string>}
*/
async function compileSvelte(options, { filename, code }, statsCollection) {
if (isSvelte5 && filename.endsWith(svelteModuleExtension)) {
const endStat = statsCollection?.start(filename);
const compiled = svelte.compileModule(code, {
filename,
generate: 'dom',
runes: true
});
if (endStat) {
endStat();
}
return compiled.js.map
? compiled.js.code + '//# sourceMappingURL=' + compiled.js.map.toUrl()
: compiled.js.code;
}

let css = options.compilerOptions.css;
if (css !== 'none') {
// TODO ideally we'd be able to externalize prebundled styles too, but for now always put them in the js
Expand All @@ -75,7 +95,7 @@ async function compileSvelte(options, { filename, code }, statsCollection) {

if (options.preprocess) {
try {
preprocessed = await preprocess(code, options.preprocess, { filename });
preprocessed = await svelte.preprocess(code, options.preprocess, { filename });
} catch (e) {
e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
throw e;
Expand All @@ -102,7 +122,7 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
}
: compileOptions;
const endStat = statsCollection?.start(filename);
const compiled = compile(finalCode, finalCompileOptions);
const compiled = svelte.compile(finalCode, finalCompileOptions);
if (endStat) {
endStat();
}
Expand Down
45 changes: 45 additions & 0 deletions packages/vite-plugin-svelte/src/utils/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,48 @@ export function buildIdParser(options) {
}
};
}
/**
* @param {import('../types/options.d.ts').ResolvedOptions} options
* @returns {import('../types/id.d.ts').ModuleIdParser}
*/
export function buildModuleIdParser(options) {
const { include, exclude, extensions } = options?.experimental?.compileModule ?? {};
const root = options.root;
const normalizedRoot = normalizePath(root);
const filter = buildFilter(include, exclude, extensions ?? ['.svelte.js', '.svelte.ts']);
return (id, ssr, timestamp = Date.now()) => {
const { filename, rawQuery } = splitId(id);
if (filter(filename)) {
return parseToSvelteModuleRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr);
}
};
}

/**
* @param {string} id
* @param {string} filename
* @param {string} rawQuery
* @param {string} root
* @param {number} timestamp
* @param {boolean} ssr
* @returns {import('../types/id.d.ts').SvelteModuleRequest | undefined}
*/
function parseToSvelteModuleRequest(id, filename, rawQuery, root, timestamp, ssr) {
const query = parseRequestQuery(rawQuery);

if (query.url || query.raw || query.direct) {
// skip requests with special vite tags
return;
}

const normalizedFilename = normalize(filename, root);

return {
id,
filename,
normalizedFilename,
query,
timestamp,
ssr
};
}
2 changes: 1 addition & 1 deletion packages/vite-plugin-svelte/src/utils/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const log = {
};

/**
* @param {import('../types/id.d.ts').SvelteRequest} svelteRequest
* @param {import('../types/id.d.ts').SvelteRequest | import('../types/id.d.ts').SvelteModuleRequest} svelteRequest
* @param {import('svelte/types/compiler/interfaces').Warning[]} warnings
* @param {import('../types/options.d.ts').ResolvedOptions} options
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/vite-plugin-svelte/src/utils/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import { isCommonDepWithoutSvelteField } from './dependencies.js';
import { VitePluginSvelteStats } from './vite-plugin-svelte-stats.js';
import { VitePluginSvelteCache } from './vite-plugin-svelte-cache.js';
import { isSvelte5 } from './svelte-version.js';

const allowedPluginOptions = new Set([
'include',
Expand Down Expand Up @@ -227,6 +228,10 @@ export function resolveOptions(preResolveOptions, viteConfig, cache) {
* @param {import('../types/options.d.ts').ResolvedOptions} options
*/
function enforceOptionsForHmr(options) {
if (isSvelte5) {
log.warn('svelte 5 does not support hmr api yet, disabling it for now');
options.hot = false;
}
if (options.hot) {
if (!options.compilerOptions.dev) {
log.warn('hmr is enabled but compilerOptions.dev is false, forcing it to true');
Expand Down
5 changes: 5 additions & 0 deletions packages/vite-plugin-svelte/src/utils/svelte-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ import { VERSION } from 'svelte/compiler';
* @type {boolean}
*/
export const isSvelte3 = VERSION.startsWith('3.');

/**
* @type {boolean}
*/
export const isSvelte5 = VERSION.startsWith('5.');

0 comments on commit 31ce52c

Please sign in to comment.