You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
5.3 KiB
TypeScript
183 lines
5.3 KiB
TypeScript
import { SvelteRequest } from './id';
|
|
import { Code, CompileData } from './compile';
|
|
import { readFileSync } from 'fs';
|
|
import { dirname } from 'path';
|
|
//eslint-disable-next-line node/no-missing-import
|
|
import { findClosestPkgJsonPath } from 'vitefu';
|
|
import { normalizePath } from 'vite';
|
|
|
|
interface PackageInfo {
|
|
name: string;
|
|
version: string;
|
|
svelte?: string;
|
|
path: string;
|
|
}
|
|
|
|
export class VitePluginSvelteCache {
|
|
private _css = new Map<string, Code>();
|
|
private _js = new Map<string, Code>();
|
|
private _dependencies = new Map<string, string[]>();
|
|
private _dependants = new Map<string, Set<string>>();
|
|
private _resolvedSvelteFields = new Map<string, string>();
|
|
private _errors = new Map<string, any>();
|
|
private _packageInfos: PackageInfo[] = [];
|
|
|
|
public update(compileData: CompileData) {
|
|
this._errors.delete(compileData.normalizedFilename);
|
|
this.updateCSS(compileData);
|
|
this.updateJS(compileData);
|
|
this.updateDependencies(compileData);
|
|
}
|
|
|
|
public has(svelteRequest: SvelteRequest) {
|
|
const id = svelteRequest.normalizedFilename;
|
|
return this._errors.has(id) || this._js.has(id) || this._css.has(id);
|
|
}
|
|
|
|
public setError(svelteRequest: SvelteRequest, error: any) {
|
|
// keep dependency info, otherwise errors in dependants would not trigger an update after fixing
|
|
// because they are no longer watched
|
|
this.remove(svelteRequest, true);
|
|
this._errors.set(svelteRequest.normalizedFilename, error);
|
|
}
|
|
|
|
private updateCSS(compileData: CompileData) {
|
|
this._css.set(compileData.normalizedFilename, compileData.compiled.css);
|
|
}
|
|
|
|
private updateJS(compileData: CompileData) {
|
|
if (!compileData.ssr) {
|
|
// do not cache SSR js
|
|
this._js.set(compileData.normalizedFilename, compileData.compiled.js);
|
|
}
|
|
}
|
|
|
|
private updateDependencies(compileData: CompileData) {
|
|
const id = compileData.normalizedFilename;
|
|
const prevDependencies = this._dependencies.get(id) || [];
|
|
const dependencies = compileData.dependencies;
|
|
this._dependencies.set(id, dependencies);
|
|
const removed = prevDependencies.filter((d) => !dependencies.includes(d));
|
|
const added = dependencies.filter((d) => !prevDependencies.includes(d));
|
|
added.forEach((d) => {
|
|
if (!this._dependants.has(d)) {
|
|
this._dependants.set(d, new Set<string>());
|
|
}
|
|
this._dependants.get(d)!.add(compileData.filename);
|
|
});
|
|
removed.forEach((d) => {
|
|
this._dependants.get(d)!.delete(compileData.filename);
|
|
});
|
|
}
|
|
|
|
public remove(svelteRequest: SvelteRequest, keepDependencies: boolean = false): boolean {
|
|
const id = svelteRequest.normalizedFilename;
|
|
let removed = false;
|
|
if (this._errors.delete(id)) {
|
|
removed = true;
|
|
}
|
|
if (this._js.delete(id)) {
|
|
removed = true;
|
|
}
|
|
if (this._css.delete(id)) {
|
|
removed = true;
|
|
}
|
|
if (!keepDependencies) {
|
|
const dependencies = this._dependencies.get(id);
|
|
if (dependencies) {
|
|
removed = true;
|
|
dependencies.forEach((d) => {
|
|
const dependants = this._dependants.get(d);
|
|
if (dependants && dependants.has(svelteRequest.filename)) {
|
|
dependants.delete(svelteRequest.filename);
|
|
}
|
|
});
|
|
this._dependencies.delete(id);
|
|
}
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
public getCSS(svelteRequest: SvelteRequest) {
|
|
return this._css.get(svelteRequest.normalizedFilename);
|
|
}
|
|
|
|
public getJS(svelteRequest: SvelteRequest) {
|
|
if (!svelteRequest.ssr) {
|
|
// SSR js isn't cached
|
|
return this._js.get(svelteRequest.normalizedFilename);
|
|
}
|
|
}
|
|
|
|
public getError(svelteRequest: SvelteRequest) {
|
|
return this._errors.get(svelteRequest.normalizedFilename);
|
|
}
|
|
|
|
public getDependants(path: string): string[] {
|
|
const dependants = this._dependants.get(path);
|
|
return dependants ? [...dependants] : [];
|
|
}
|
|
|
|
public getResolvedSvelteField(name: string, importer?: string): string | void {
|
|
return this._resolvedSvelteFields.get(this._getResolvedSvelteFieldKey(name, importer));
|
|
}
|
|
|
|
public hasResolvedSvelteField(name: string, importer?: string) {
|
|
return this._resolvedSvelteFields.has(this._getResolvedSvelteFieldKey(name, importer));
|
|
}
|
|
public setResolvedSvelteField(
|
|
importee: string,
|
|
importer: string | undefined = undefined,
|
|
resolvedSvelte: string
|
|
) {
|
|
this._resolvedSvelteFields.set(
|
|
this._getResolvedSvelteFieldKey(importee, importer),
|
|
resolvedSvelte
|
|
);
|
|
}
|
|
|
|
private _getResolvedSvelteFieldKey(importee: string, importer?: string): string {
|
|
return importer ? `${importer} > ${importee}` : importee;
|
|
}
|
|
|
|
public async getPackageInfo(file: string): Promise<PackageInfo> {
|
|
let info = this._packageInfos.find((pi) => file.startsWith(pi.path));
|
|
if (!info) {
|
|
info = await findPackageInfo(file);
|
|
this._packageInfos.push(info);
|
|
}
|
|
return info;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* utility to get some info from the closest package.json with a "name" set
|
|
*
|
|
* @param {string} file to find info for
|
|
* @returns {PackageInfo}
|
|
*/
|
|
async function findPackageInfo(file: string): Promise<PackageInfo> {
|
|
const info: PackageInfo = {
|
|
name: '$unknown',
|
|
version: '0.0.0-unknown',
|
|
path: '$unknown'
|
|
};
|
|
let path = await findClosestPkgJsonPath(file, (pkgPath) => {
|
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
if (pkg.name != null) {
|
|
info.name = pkg.name;
|
|
if (pkg.version != null) {
|
|
info.version = pkg.version;
|
|
}
|
|
info.svelte = pkg.svelte;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
// return normalized path with appended '/' so .startsWith works for future file checks
|
|
path = normalizePath(dirname(path ?? file)) + '/';
|
|
info.path = path;
|
|
return info;
|
|
}
|