Skip to content

Commit

Permalink
feat: reduce use of cache in vite-plugin-svelte (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikg authored Apr 15, 2021
1 parent 46c2750 commit 113bb7d
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 88 deletions.
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

0 comments on commit 113bb7d

Please sign in to comment.