Add svelte as a view engine to swaf #33
@ -30,10 +30,7 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
|
|
||||||
private readonly fileCache: FileCache = new FileCache();
|
private readonly fileCache: FileCache = new FileCache();
|
||||||
private readonly dependencyCache: Record<string, Set<string>> = {};
|
private readonly dependencyCache: Record<string, Set<string>> = {};
|
||||||
private readonly backendCallsCache: Record<string, {
|
private readonly preprocessingCache: Record<string, PreprocessingCacheEntry> = {};
|
||||||
replacedBackendCall: string,
|
|
||||||
backendCalls: string[],
|
|
||||||
}> = {};
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
targetDir: string,
|
targetDir: string,
|
||||||
@ -43,12 +40,12 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onFileChange(file: string): Promise<void> {
|
public async onFileChange(file: string): Promise<void> {
|
||||||
delete this.backendCallsCache[this.toCanonicalName(file)];
|
delete this.preprocessingCache[this.toCanonicalName(file)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onFileRemove(file: string): Promise<void> {
|
public async onFileRemove(file: string): Promise<void> {
|
||||||
const canonicalName = this.toCanonicalName(file);
|
const canonicalName = this.toCanonicalName(file);
|
||||||
delete this.backendCallsCache[canonicalName];
|
delete this.preprocessingCache[canonicalName];
|
||||||
delete this.dependencyCache[canonicalName];
|
delete this.dependencyCache[canonicalName];
|
||||||
Object.values(this.dependencyCache).forEach(set => set.delete(canonicalName));
|
Object.values(this.dependencyCache).forEach(set => set.delete(canonicalName));
|
||||||
await super.onFileRemove(file);
|
await super.onFileRemove(file);
|
||||||
@ -124,14 +121,14 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
|
|
||||||
const allBackendCalls: string[] = [];
|
const allBackendCalls: string[] = [];
|
||||||
for (const dependency of this.resolveDependencies(source, canonicalName)) {
|
for (const dependency of this.resolveDependencies(source, canonicalName)) {
|
||||||
allBackendCalls.push(...(await this.replaceBackendCalls(dependency)).backendCalls);
|
allBackendCalls.push(...(await this.preprocess(dependency)).backendCalls);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {replacedBackendCall, backendCalls} = await this.replaceBackendCalls(canonicalName, source);
|
const {backendCalls, code} = await this.preprocess(canonicalName, source);
|
||||||
allBackendCalls.push(...backendCalls);
|
allBackendCalls.push(...backendCalls);
|
||||||
|
|
||||||
// Server Side Render (initial HTML and CSS, no-js)
|
// Server Side Render (initial HTML and CSS, no-js)
|
||||||
const ssr = await this.compileSsr(canonicalName, intermediateFile, replacedBackendCall);
|
const ssr = await this.compileSsr(canonicalName, intermediateFile, code);
|
||||||
|
|
||||||
const separator = SvelteViewEngine.getPreCompileSeparator(canonicalName);
|
const separator = SvelteViewEngine.getPreCompileSeparator(canonicalName);
|
||||||
const finalCode = [
|
const finalCode = [
|
||||||
@ -176,34 +173,59 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async replaceBackendCalls(canonicalViewName: string, code?: string): Promise<{
|
private async preprocess(canonicalName: string, code?: string): Promise<PreprocessingCacheEntry> {
|
||||||
replacedBackendCall: string,
|
|
||||||
backendCalls: string[],
|
|
||||||
}> {
|
|
||||||
// Cache
|
// Cache
|
||||||
if (Object.keys(this.backendCallsCache).indexOf(canonicalViewName) >= 0) {
|
if (Object.keys(this.preprocessingCache).indexOf(canonicalName) >= 0) {
|
||||||
return this.backendCallsCache[canonicalViewName];
|
return this.preprocessingCache[canonicalName];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const file = await this.resolveFileFromCanonicalName(canonicalName);
|
||||||
|
logger.info(canonicalName + ' > ', `Preprocessing ${file}`);
|
||||||
|
|
||||||
// mkdir output file dir
|
// mkdir output file dir
|
||||||
const outputFile = path.join(this.targetDir, canonicalViewName);
|
const outputFile = path.join(this.targetDir, canonicalName);
|
||||||
await fs.mkdir(path.dirname(outputFile), {recursive: true});
|
await fs.mkdir(path.dirname(outputFile), {recursive: true});
|
||||||
|
|
||||||
// Read source file if code was not already provided
|
// Read source file if code was not already provided
|
||||||
if (!code) {
|
if (!code) {
|
||||||
const file = await this.resolveFileFromCanonicalName(canonicalViewName);
|
|
||||||
code = await this.fileCache.get(file, !config.get<boolean>('view.cache'));
|
code = await this.fileCache.get(file, !config.get<boolean>('view.cache'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace backend calls
|
||||||
|
const replacedBackendCalls = await this.replaceBackendCalls(canonicalName, code);
|
||||||
|
|
||||||
|
// Preprocess svelte
|
||||||
|
logger.info(canonicalName + ' > ', 'Svelte preprocessing');
|
||||||
|
const preprocessed = await preprocess(
|
||||||
|
replacedBackendCalls.code,
|
||||||
|
sveltePreprocess({
|
||||||
|
typescript: {
|
||||||
|
tsconfigFile: 'tsconfig.svelte.json',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
filename: outputFile,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write to output file
|
||||||
|
await fs.writeFile(outputFile, preprocessed.code);
|
||||||
|
|
||||||
|
return this.preprocessingCache[canonicalName] = {
|
||||||
|
backendCalls: replacedBackendCalls.backendCalls,
|
||||||
|
code: preprocessed.code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async replaceBackendCalls(canonicalName: string, code: string): Promise<PreprocessingCacheEntry> {
|
||||||
|
logger.info(canonicalName + ' > ', 'Replacing backend calls');
|
||||||
|
|
||||||
// Skip replace if there is no swaf export
|
// Skip replace if there is no swaf export
|
||||||
if (!code.match(/export[ \n]+let[ \n]+locals[ \n]*=[ \n]*{[ \n]*}/)) {
|
if (!code.match(/export[ \n]+let[ \n]+locals[ \n]*=[ \n]*{[ \n]*}/)) {
|
||||||
const generated = {
|
return {
|
||||||
replacedBackendCall: code,
|
|
||||||
backendCalls: [],
|
backendCalls: [],
|
||||||
|
code: code,
|
||||||
};
|
};
|
||||||
await fs.writeFile(outputFile, generated.replacedBackendCall);
|
|
||||||
this.backendCallsCache[canonicalViewName] = generated;
|
|
||||||
return generated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -251,14 +273,10 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
}
|
}
|
||||||
output = output.split(BACKEND_CODE_PREFIX_TEMPORARY_HOLDER).join(BACKEND_CODE_PREFIX);
|
output = output.split(BACKEND_CODE_PREFIX_TEMPORARY_HOLDER).join(BACKEND_CODE_PREFIX);
|
||||||
|
|
||||||
const generated = {
|
return {
|
||||||
replacedBackendCall: output,
|
|
||||||
backendCalls: [...backendCalls],
|
backendCalls: [...backendCalls],
|
||||||
|
code: output,
|
||||||
};
|
};
|
||||||
await fs.writeFile(outputFile, generated.replacedBackendCall);
|
|
||||||
this.backendCallsCache[canonicalViewName] = generated;
|
|
||||||
|
|
||||||
return generated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async compileSsr(canonicalName: string, file: string, code: string): Promise<{
|
private async compileSsr(canonicalName: string, file: string, code: string): Promise<{
|
||||||
@ -266,23 +284,9 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
css: CssResult,
|
css: CssResult,
|
||||||
html: string,
|
html: string,
|
||||||
}> {
|
}> {
|
||||||
// Svelte preprocess
|
|
||||||
logger.info(canonicalName + ' > ', 'Preprocessing svelte', file);
|
|
||||||
const preprocessed = await preprocess(
|
|
||||||
code,
|
|
||||||
sveltePreprocess({
|
|
||||||
typescript: {
|
|
||||||
tsconfigFile: 'tsconfig.svelte.json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
filename: file,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Svelte compile
|
// Svelte compile
|
||||||
logger.info(canonicalName + ' > ', 'Compiling svelte ssr', file);
|
logger.info(canonicalName + ' > ', 'Compiling svelte ssr', file);
|
||||||
const svelteSsr = compile(preprocessed.code, {
|
const svelteSsr = compile(code, {
|
||||||
dev: config.get<boolean>('view.dev'),
|
dev: config.get<boolean>('view.dev'),
|
||||||
generate: 'ssr',
|
generate: 'ssr',
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
@ -299,3 +303,7 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PreprocessingCacheEntry = {
|
||||||
|
backendCalls: string[],
|
||||||
|
code: string,
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user