Skip to content

feat: reduce use of cache in vite-plugin-svelte #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eight-taxis-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/vite-plugin-svelte': minor
---

Reduced cache usage, share css cache between SSR and client
40 changes: 20 additions & 20 deletions packages/vite-plugin-svelte/src/handleHotUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ModuleNode, HmrContext } from 'vite';
import { CompileData } from './utils/compile';
import { Code, CompileData } from './utils/compile';
import { log } from './utils/log';
import { SvelteRequest } from './utils/id';
import { VitePluginSvelteCache } from './utils/VitePluginSvelteCache';
import { ResolvedOptions } from './utils/options';

/**
* Vite-specific HMR handling
Expand All @@ -11,34 +12,33 @@ export async function handleHotUpdate(
compileSvelte: Function,
ctx: HmrContext,
svelteRequest: SvelteRequest,
cache: VitePluginSvelteCache
cache: VitePluginSvelteCache,
options: Partial<ResolvedOptions>
): Promise<ModuleNode[] | void> {
const { read, server } = ctx;
const cachedCompileData = cache.getCompileData(svelteRequest, false);
if (!cachedCompileData) {

const cachedJS = cache.getJS(svelteRequest);
if (!cachedJS) {
// file hasn't been requested yet (e.g. async component)
log.debug(`handleHotUpdate first call ${svelteRequest.id}`);
return;
}
const cachedCss = cache.getCSS(svelteRequest);

const content = await read();
const compileData: CompileData = await compileSvelte(
svelteRequest,
content,
cachedCompileData.options
);
cache.setCompileData(compileData);
const compileData: CompileData = await compileSvelte(svelteRequest, content, options);
cache.update(compileData);

const affectedModules = new Set<ModuleNode | undefined>();

const cssModule = server.moduleGraph.getModuleById(svelteRequest.cssId);
const mainModule = server.moduleGraph.getModuleById(svelteRequest.id);
if (cssModule && cssChanged(cachedCompileData, compileData)) {
if (cssModule && cssChanged(cachedCss, compileData.compiled.css)) {
log.debug('handleHotUpdate css changed');
affectedModules.add(cssModule);
}

if (mainModule && jsChanged(cachedCompileData, compileData)) {
if (mainModule && jsChanged(cachedJS, compileData.compiled.js, svelteRequest.filename)) {
log.debug('handleHotUpdate js changed');
affectedModules.add(mainModule);
}
Expand All @@ -56,27 +56,27 @@ export async function handleHotUpdate(
return result;
}

function cssChanged(prev: CompileData, next: CompileData): boolean {
return !isCodeEqual(prev.compiled.css?.code, next.compiled.css?.code);
function cssChanged(prev?: Code, next?: Code): boolean {
return !isCodeEqual(prev?.code, next?.code);
}

function jsChanged(prev: CompileData, next: CompileData): boolean {
const prevJs = prev.compiled.js.code;
const nextJs = next.compiled.js.code;
function jsChanged(prev?: Code, next?: Code, filename?: string): boolean {
const prevJs = prev?.code;
const nextJs = next?.code;
const isStrictEqual = isCodeEqual(prevJs, nextJs);
if (isStrictEqual) {
return false;
}
const isLooseEqual = isCodeEqual(normalizeJsCode(prevJs), normalizeJsCode(nextJs));
if (!isStrictEqual && isLooseEqual) {
log.warn(
`ignoring compiler output js change for ${next.filename} as it is equal to previous output after normalization`
`ignoring compiler output js change for ${filename} as it is equal to previous output after normalization`
);
}
return !isLooseEqual;
}

function isCodeEqual(prev: string, next: string): boolean {
function isCodeEqual(prev?: string, next?: string): boolean {
if (!prev && !next) {
return true;
}
Expand All @@ -93,7 +93,7 @@ function isCodeEqual(prev: string, next: string): boolean {
* 2) ... maybe more (or less) in the future
* @param code
*/
function normalizeJsCode(code: string): string {
function normalizeJsCode(code?: string): string | undefined {
if (!code) {
return code;
}
Expand Down
29 changes: 12 additions & 17 deletions packages/vite-plugin-svelte/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ export default function vitePluginSvelte(inlineOptions?: Partial<Options>): Plug
//
if (query.svelte) {
if (query.type === 'style') {
const compileData = cache.getCompileData(svelteRequest, false);
if (compileData?.compiled?.css) {
const css = cache.getCSS(svelteRequest);
if (css) {
log.debug(`load returns css for ${filename}`);
return compileData.compiled.css;
return css;
}
}
}
Expand Down Expand Up @@ -183,26 +183,21 @@ export default function vitePluginSvelte(inlineOptions?: Partial<Options>): Plug
}
log.debug('transform', svelteRequest);
const { filename, query } = svelteRequest;
const cachedCompileData = cache.getCompileData(svelteRequest, false);

if (query.svelte) {
// tagged svelte request, use cache
if (query.type === 'style' && cachedCompileData?.compiled?.css) {
log.debug(`transform returns css for ${filename}`);
return cachedCompileData.compiled.css;
if (query.type === 'style') {
const css = cache.getCSS(svelteRequest);
if (css) {
log.debug(`transform returns css for ${filename}`);
return css; // TODO return code arg instead? it's the code from load hook.
}
}
log.error('failed to transform tagged svelte request', svelteRequest);
throw new Error(`failed to transform tagged svelte request for id ${id}`);
}

if (cachedCompileData && !options.disableTransformCache) {
log.debug(`transform returns cached js for ${filename}`);
return cachedCompileData.compiled.js;
}

// first request, compile here
const compileData = await compileSvelte(svelteRequest, code, options);
cache.setCompileData(compileData);
cache.update(compileData);

log.debug(`transform returns compiled js for ${filename}`);
return compileData.compiled.js;
},
Expand All @@ -216,7 +211,7 @@ export default function vitePluginSvelte(inlineOptions?: Partial<Options>): Plug
return;
}
log.debug('handleHotUpdate', svelteRequest);
return handleHotUpdate(compileSvelte, ctx, svelteRequest, cache);
return handleHotUpdate(compileSvelte, ctx, svelteRequest, cache, options);
},

// eslint-disable-next-line no-unused-vars
Expand Down
44 changes: 17 additions & 27 deletions packages/vite-plugin-svelte/src/utils/VitePluginSvelteCache.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
import { SvelteRequest } from './id';
import { CompileData } from './compile';
import { Code, CompileData } from './compile';

export class VitePluginSvelteCache {
private _compile = new Map<string, CompileData>();
private _compileSSR = new Map<string, CompileData>();
private _css = new Map<string, Code>();
private _js = new Map<string, Code>();

private selectCache(ssr: boolean): Map<string, CompileData> {
return ssr ? this._compileSSR : this._compile;
}

public getCompileData(
svelteRequest: SvelteRequest,
errorOnMissing = true
): CompileData | undefined {
const cache = this.selectCache(svelteRequest.ssr);
const id = svelteRequest.normalizedFilename;
if (cache.has(id)) {
return cache.get(id)!;
}
if (errorOnMissing) {
throw new Error(
`${id} has no corresponding entry in the ${svelteRequest.ssr ? 'ssr' : ''}cache. ` +
`This is a @sveltejs/vite-plugin-svelte internal error, please open an issue.`
);
public update(compileData: CompileData) {
const id = compileData.normalizedFilename;
this._css.set(id, compileData.compiled.css);
if (!compileData.ssr) {
// do not cache SSR js
this._js.set(id, compileData.compiled.js);
}
}

public setCompileData(compileData: CompileData) {
const cache = this.selectCache(!!compileData.ssr);
const id = compileData.normalizedFilename;
cache.set(id, compileData);
public getCSS(svelteRequest: SvelteRequest) {
return this._css.get(svelteRequest.normalizedFilename);
}

// TODO accessors by id/url?
// TODO expose on plugin instance?
public getJS(svelteRequest: SvelteRequest) {
if (!svelteRequest.ssr) {
// SSR js isn't cached
return this._js.get(svelteRequest.normalizedFilename);
}
}
}
19 changes: 2 additions & 17 deletions packages/vite-plugin-svelte/src/utils/compile.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CompileOptions, PreprocessorGroup, Processed, ResolvedOptions } from './options';
import { CompileOptions, PreprocessorGroup, ResolvedOptions } from './options';
import { compile, preprocess, walk } from 'svelte/compiler';
// @ts-ignore
import { createMakeHot } from 'svelte-hmr';
Expand Down Expand Up @@ -74,20 +74,11 @@ const _createCompileSvelte = (makeHot: Function, extraPreprocessors: Preprocesso

compiled.js.dependencies = dependencies;

// return everything that was created during preprocess/compile
const result = {
filename,
return {
normalizedFilename,
cssId,
code,
preprocessed,
compiled,
compilerOptions: finalCompilerOptions,
options,
ssr
};

return result;
};

function buildMakeHot(options: ResolvedOptions) {
Expand Down Expand Up @@ -142,13 +133,7 @@ export interface Compiled {
}

export interface CompileData {
filename: string;
normalizedFilename: string;
cssId: string;
code: string;
preprocessed?: Processed;
compiled: Compiled;
compilerOptions: CompileOptions;
options: Partial<ResolvedOptions>;
ssr: boolean | undefined;
}
7 changes: 0 additions & 7 deletions packages/vite-plugin-svelte/src/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const knownOptions = new Set([
'compilerOptions',
'preprocess',
'hot',
'disableTransformCache',
'disableCssHmr',
'useVitePreprocess'
]);
Expand Down Expand Up @@ -300,12 +299,6 @@ export interface Options {
*/
disableCssHmr?: boolean;

/**
* do not return cached transform data
* @default false
*/
disableTransformCache?: boolean;

/**
* use vite as extra css preprocessor EXPERIMENTAL!
* @default false
Expand Down