Add svelte as a view engine to swaf #33
@ -48,6 +48,7 @@
|
||||
view: {
|
||||
cache: false,
|
||||
enable_asset_cache: false,
|
||||
dev: true,
|
||||
},
|
||||
magic_link: {
|
||||
validity_period: 20,
|
||||
|
@ -21,5 +21,6 @@
|
||||
view: {
|
||||
cache: true,
|
||||
enable_asset_cache: true,
|
||||
dev: false,
|
||||
},
|
||||
}
|
||||
|
@ -18,11 +18,13 @@
|
||||
"prepare-sources": "node scripts/prepare-sources.js",
|
||||
"compile": "yarn clean && tsc",
|
||||
"build": "yarn prepare-sources && yarn compile && node scripts/dist.js",
|
||||
"dev": "yarn prepare-sources && concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"maildev\"",
|
||||
"dev": "yarn prepare-sources && concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon -i public\" \"maildev\"",
|
||||
"lint": "eslint .",
|
||||
"release": "yarn build && yarn lint && yarn test && cd dist && yarn publish"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.1.0",
|
||||
"@rollup/plugin-node-resolve": "^11.2.0",
|
||||
"@sveltejs/eslint-config": "sveltejs/eslint-config",
|
||||
"@tsconfig/svelte": "^1.0.10",
|
||||
"@types/compression": "^1.7.0",
|
||||
@ -61,6 +63,11 @@
|
||||
"node-sass": "^5.0.0",
|
||||
"nodemon": "^2.0.6",
|
||||
"require-from-string": "^2.0.2",
|
||||
"rollup": "^2.42.3",
|
||||
"rollup-plugin-css-only": "^3.1.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-svelte": "^7.1.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sass": "^1.32.8",
|
||||
"supertest": "^6.0.0",
|
||||
"svelte": "^3.35.0",
|
||||
|
@ -76,7 +76,7 @@ export default class TestApp extends Application {
|
||||
|
||||
// Dynamic views and routes
|
||||
this.use(new NunjucksComponent(['test/views', 'views']));
|
||||
this.use(new FrontendToolsComponent('public', 'views', 'build'));
|
||||
this.use(new FrontendToolsComponent('public', 'build'));
|
||||
this.use(new PreviousUrlComponent());
|
||||
|
||||
// Services
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Router} from "express";
|
||||
import {Express, Router} from "express";
|
||||
import * as fs from "fs";
|
||||
import path from "path";
|
||||
import config from "config";
|
||||
@ -6,53 +6,102 @@ import ApplicationComponent from "../ApplicationComponent";
|
||||
import {logger} from "../Logger";
|
||||
import {ServerError} from "../HttpError";
|
||||
import * as crypto from "crypto";
|
||||
import {compile, preprocess} from "svelte/compiler";
|
||||
import requireFromString from "require-from-string";
|
||||
import "svelte/register";
|
||||
import {compile, preprocess} from "svelte/compiler";
|
||||
import {sveltePreprocess} from "svelte-preprocess/dist/autoProcess";
|
||||
import chokidar from "chokidar";
|
||||
import {CssResult} from "svelte/types/compiler/interfaces";
|
||||
import {PreRenderedChunk, rollup, RollupBuild, RollupOptions, RollupWatcher, RollupWatchOptions, watch} from "rollup";
|
||||
import svelte from "rollup-plugin-svelte";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import commonjs from "@rollup/plugin-commonjs";
|
||||
import {terser} from "rollup-plugin-terser";
|
||||
import cssOnlyRollupPlugin from "rollup-plugin-css-only";
|
||||
import livereloadRollupPlugin from "rollup-plugin-livereload";
|
||||
|
||||
const BACKEND_CODE_PREFIX = 'swaf.';
|
||||
const COMPILED_SVELTE_EXTENSION = '.swafview';
|
||||
|
||||
export default class FrontendToolsComponent extends ApplicationComponent {
|
||||
public static getSveltePreCompileSeparator(file: string): string {
|
||||
return '\n---' + crypto.createHash('sha1').update(path.basename(path.resolve(file))).digest('base64') + '---\n';
|
||||
public static getSveltePreCompileSeparator(canonicalViewName: string): string {
|
||||
return '\n---' +
|
||||
crypto.createHash('sha1')
|
||||
.update(path.basename(path.resolve(canonicalViewName)))
|
||||
.digest('base64') +
|
||||
'---\n';
|
||||
}
|
||||
|
||||
private content: Map<string, string> = new Map();
|
||||
private readonly viewPaths: string[];
|
||||
private readonly svelteDevViewsPath: string;
|
||||
|
||||
private readonly dependencyCache: Record<string, Set<string>> = {};
|
||||
private readonly backendCodeCache: Record<string, {
|
||||
backendReplacedCode: string,
|
||||
backendLines: string[],
|
||||
}> = {};
|
||||
private readonly fileCache: Record<string, string> = {};
|
||||
|
||||
private rollup?: RollupBuild | RollupWatcher;
|
||||
|
||||
public constructor(
|
||||
private readonly publicAssetsPath: string,
|
||||
private readonly svelteViewsPath: string,
|
||||
private readonly svelteOutputPath: string,
|
||||
...viewPaths: string[]
|
||||
) {
|
||||
super();
|
||||
this.viewPaths = [
|
||||
...viewPaths.map(p => path.resolve(p)),
|
||||
path.resolve(__dirname, '../../../views'),
|
||||
path.resolve(__dirname, '../../views'),
|
||||
path.resolve(__dirname, '../views'),
|
||||
];
|
||||
this.svelteDevViewsPath = path.resolve('views');
|
||||
|
||||
if (!fs.existsSync(svelteOutputPath)) {
|
||||
fs.mkdirSync(svelteOutputPath);
|
||||
}
|
||||
}
|
||||
|
||||
public async start(): Promise<void> {
|
||||
public async start(app: Express): Promise<void> {
|
||||
await this.cachePublicAssets();
|
||||
|
||||
await this.preCompileSvelteViews();
|
||||
|
||||
const watcher = chokidar.watch(this.svelteViewsPath, {persistent: true});
|
||||
const watcher = chokidar.watch(this.svelteDevViewsPath, {persistent: true});
|
||||
watcher.on('ready', () => {
|
||||
logger.debug('Watching svelte assets for changes');
|
||||
|
||||
watcher.on('add', (path) => {
|
||||
this.preCompileSvelte(path)
|
||||
.catch(logger.error);
|
||||
if (path.endsWith('.svelte')) {
|
||||
this.resetBundle()
|
||||
.then(() => this.preCompileSvelte(path, true))
|
||||
.then(() => this.bundle(...Object.keys(this.backendCodeCache)))
|
||||
.catch(err => logger.error(err));
|
||||
}
|
||||
});
|
||||
|
||||
watcher.on('change', (path) => {
|
||||
this.preCompileSvelte(path)
|
||||
.catch(logger.error);
|
||||
if (path.endsWith('.svelte')) {
|
||||
delete this.backendCodeCache[this.toCanonicalViewName(path)];
|
||||
this.preCompileSvelte(path, true)
|
||||
.then(() => this.bundle(...Object.keys(this.backendCodeCache)))
|
||||
.catch(err => logger.error(err));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.engine('svelte', (path, options, callback) => {
|
||||
this.renderSvelte(path, options as Record<string, unknown>, callback)
|
||||
.catch(err => callback(err));
|
||||
});
|
||||
app.set('views', this.viewPaths);
|
||||
app.set('view engine', 'svelte');
|
||||
}
|
||||
|
||||
public async stop(): Promise<void> {
|
||||
await this.resetBundle();
|
||||
}
|
||||
|
||||
private async cachePublicAssets(): Promise<void> {
|
||||
@ -66,11 +115,12 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
||||
|
||||
private async preCompileSvelteViews(): Promise<void> {
|
||||
logger.info('Pre-compiling svelte views...');
|
||||
await this.forEachFileInDirRecursively(this.svelteViewsPath, async file => {
|
||||
await this.forEachFileInDirRecursively(this.svelteDevViewsPath, async file => {
|
||||
if (file.endsWith('.svelte')) {
|
||||
await this.preCompileSvelte(file);
|
||||
}
|
||||
});
|
||||
await this.bundle(...Object.keys(this.backendCodeCache));
|
||||
}
|
||||
|
||||
public async handle(router: Router): Promise<void> {
|
||||
@ -124,8 +174,12 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
||||
});
|
||||
}
|
||||
|
||||
private async preCompileSvelte(file: string): Promise<void> {
|
||||
logger.debug('Pre-compiling', file);
|
||||
private async preCompileSvelte(file: string, alsoCompileDependents: boolean = false): Promise<void> {
|
||||
file = path.relative('.', file);
|
||||
const canonicalViewName = this.toCanonicalViewName(file);
|
||||
const intermediateFile = path.join(this.svelteOutputPath, canonicalViewName);
|
||||
|
||||
logger.debug(canonicalViewName + ' > ', 'Pre-compiling', file, '->', intermediateFile);
|
||||
const source = await new Promise<string>((resolve, reject) => {
|
||||
fs.readFile(file, (err, data) => {
|
||||
if (err) return reject(err);
|
||||
@ -134,54 +188,127 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
||||
});
|
||||
});
|
||||
|
||||
const {backendReplacedCode, backendLines} = this.replaceBackendCode(source);
|
||||
const allBackendLines: string[] = [];
|
||||
for (const dependency of this.resolveDependencies(source, canonicalViewName)) {
|
||||
allBackendLines.push(...(await this.replaceBackendCode(dependency)).backendLines);
|
||||
}
|
||||
|
||||
const preprocessed = await this.preprocessSvelte(backendReplacedCode, file);
|
||||
const {backendReplacedCode, backendLines} = await this.replaceBackendCode(canonicalViewName, source);
|
||||
allBackendLines.push(...backendLines);
|
||||
|
||||
const preprocessedCode = await this.preProcessSvelte(backendReplacedCode, intermediateFile, canonicalViewName);
|
||||
|
||||
// Server Side Render (initial HTML, no-js)
|
||||
const ssr = this.compileSvelteSsr(preprocessed.code, file, preprocessed.sourcemap);
|
||||
const ssr = this.compileSvelteSsr(preprocessedCode, intermediateFile, canonicalViewName);
|
||||
|
||||
// Actual svelte
|
||||
const svelte = this.compileSvelteJS(preprocessed.code, preprocessed.sourcemap);
|
||||
|
||||
const separator = FrontendToolsComponent.getSveltePreCompileSeparator(file);
|
||||
const separator = FrontendToolsComponent.getSveltePreCompileSeparator(canonicalViewName);
|
||||
const finalCode = [
|
||||
[...backendLines.values()].join('\n'),
|
||||
[...new Set<string>(allBackendLines).values()].join('\n'),
|
||||
ssr.head,
|
||||
ssr.html,
|
||||
ssr.css.code,
|
||||
ssr.css.map,
|
||||
svelte.code,
|
||||
svelte.map,
|
||||
].join(separator);
|
||||
|
||||
const newFile = path.join(this.svelteOutputPath, path.basename(file) + COMPILED_SVELTE_EXTENSION);
|
||||
await new Promise<void>((resolve, reject) => fs.writeFile(newFile, finalCode, err => {
|
||||
const swafViewFile = path.join(this.svelteOutputPath, canonicalViewName + COMPILED_SVELTE_EXTENSION);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
fs.mkdir(path.dirname(swafViewFile), {recursive: true}, (err) => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await new Promise<void>((resolve, reject) => fs.writeFile(swafViewFile, finalCode, err => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
}));
|
||||
|
||||
if (alsoCompileDependents && Object.keys(this.dependencyCache).indexOf(canonicalViewName) >= 0) {
|
||||
logger.debug(canonicalViewName + ' > ', 'Compiling dependents...');
|
||||
for (const dependent of [...this.dependencyCache[canonicalViewName]]) {
|
||||
await this.preCompileSvelte(await this.resolveViewFromCanonicalName(dependent), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private replaceBackendCode(code: string): {
|
||||
private resolveDependencies(source: string, canonicalViewName: string): string[] {
|
||||
const dependencies: string[] = [];
|
||||
|
||||
for (const match of source.matchAll(/import .+ from ['"](.+?\.svelte)['"];/gm)) {
|
||||
dependencies.push(path.join(path.dirname(canonicalViewName), match[1]));
|
||||
}
|
||||
|
||||
// Clear existing links from cache
|
||||
for (const dependency of Object.keys(this.dependencyCache)) {
|
||||
this.dependencyCache[dependency].delete(canonicalViewName);
|
||||
}
|
||||
|
||||
// Add new links to cache
|
||||
for (const dependency of dependencies) {
|
||||
if (Object.keys(this.dependencyCache).indexOf(dependency) < 0) {
|
||||
this.dependencyCache[dependency] = new Set<string>();
|
||||
}
|
||||
this.dependencyCache[dependency].add(canonicalViewName);
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
private async replaceBackendCode(canonicalViewName: string, code?: string): Promise<{
|
||||
backendReplacedCode: string,
|
||||
backendLines: string[],
|
||||
} {
|
||||
}> {
|
||||
// Cache
|
||||
if (Object.keys(this.backendCodeCache).indexOf(canonicalViewName) >= 0) {
|
||||
return this.backendCodeCache[canonicalViewName];
|
||||
}
|
||||
|
||||
const outputFile = path.join(this.svelteOutputPath, canonicalViewName);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
fs.mkdir(path.dirname(outputFile), {recursive: true}, (err) => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// Read source file if code was not already provided
|
||||
if (!code) {
|
||||
const file = await this.resolveViewFromCanonicalName(canonicalViewName);
|
||||
code = await new Promise<string>((resolve, reject) => {
|
||||
fs.readFile(file, (err, data) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
resolve(data.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Skip replace if there is no swaf export
|
||||
if (code.indexOf(`export let swaf = {};`) < 0) {
|
||||
return {
|
||||
const generated = {
|
||||
backendReplacedCode: code,
|
||||
backendLines: [],
|
||||
};
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
fs.writeFile(outputFile, generated.backendReplacedCode, (err) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
this.backendCodeCache[canonicalViewName] = generated;
|
||||
return generated;
|
||||
}
|
||||
|
||||
|
||||
let output = code;
|
||||
const backendLines = new Set<string>();
|
||||
|
||||
let index = 0;
|
||||
while ((index = code.indexOf(BACKEND_CODE_PREFIX, index + 1)) >= 0) {
|
||||
while ((index = output.indexOf(BACKEND_CODE_PREFIX, index + 1)) >= 0) {
|
||||
// Escaping
|
||||
if (index > 0 && code[index - 1] === '\\') {
|
||||
const isEscapingEscaped = index > 1 && code[index - 2] === '\\';
|
||||
code = code.substring(0, index - 1 - (isEscapingEscaped ? 1 : 0)) +
|
||||
code.substring(index, code.length);
|
||||
if (index > 0 && output[index - 1] === '\\') {
|
||||
const isEscapingEscaped: boolean = index > 1 && output[index - 2] === '\\';
|
||||
output = output.substring(0, index - 1 - (isEscapingEscaped ? 1 : 0)) +
|
||||
output.substring(index, output.length);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -189,38 +316,50 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
||||
let endIndex = startIndex;
|
||||
let struct = 0;
|
||||
|
||||
while (endIndex < code.length) {
|
||||
if (['(', '[', '{'].indexOf(code[endIndex]) >= 0) struct++;
|
||||
if ([')', ']', '}'].indexOf(code[endIndex]) >= 0) {
|
||||
while (endIndex < output.length) {
|
||||
if (['(', '[', '{'].indexOf(output[endIndex]) >= 0) struct++;
|
||||
if ([')', ']', '}'].indexOf(output[endIndex]) >= 0) {
|
||||
struct--;
|
||||
if (struct <= 0) {
|
||||
if (struct === 0) endIndex++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ([' ', '\n', '<'].indexOf(code[endIndex]) >= 0 && struct === 0) break;
|
||||
if ([' ', '\n', '<'].indexOf(output[endIndex]) >= 0 && struct === 0) break;
|
||||
endIndex++;
|
||||
}
|
||||
|
||||
const backendLine = code.substring(startIndex, endIndex);
|
||||
let backendLine = output.substring(startIndex, endIndex);
|
||||
if (backendLine.match(/([^()]+)\((.+?)\)/)) {
|
||||
backendLine = backendLine.replace(/([^()]+)\((.+?)\)/, "'$1', [$2]");
|
||||
} else {
|
||||
backendLine = backendLine.replace(/([^()]+)/, "'$1'");
|
||||
}
|
||||
|
||||
backendLines.add(backendLine);
|
||||
code = code.substring(0, index) +
|
||||
'swaf(`' + backendLine.replace(/([^\\])`/, '$1\\`') + '`)' +
|
||||
code.substring(endIndex, code.length);
|
||||
output = output.substring(0, index) +
|
||||
'swaf(' + backendLine + ')' +
|
||||
output.substring(endIndex, output.length);
|
||||
}
|
||||
|
||||
logger.silly('Replaced backend code');
|
||||
|
||||
return {
|
||||
backendReplacedCode: code,
|
||||
const generated = {
|
||||
backendReplacedCode: output,
|
||||
backendLines: [...backendLines],
|
||||
};
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
fs.writeFile(outputFile, generated.backendReplacedCode, (err) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
this.backendCodeCache[canonicalViewName] = generated;
|
||||
|
||||
return generated;
|
||||
}
|
||||
|
||||
private async preprocessSvelte(code: string, filename: string): Promise<{
|
||||
code: string,
|
||||
sourcemap?: SourceMap,
|
||||
}> {
|
||||
private async preProcessSvelte(code: string, filename: string, canonicalViewName: string): Promise<string> {
|
||||
logger.debug(canonicalViewName + ' > ', 'Preprocessing svelte', filename);
|
||||
const preprocessed = await preprocess(
|
||||
code,
|
||||
sveltePreprocess({
|
||||
@ -233,42 +372,249 @@ export default class FrontendToolsComponent extends ApplicationComponent {
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
code: preprocessed.code,
|
||||
sourcemap: preprocessed.map as (SourceMap | undefined),
|
||||
};
|
||||
return preprocessed.code;
|
||||
}
|
||||
|
||||
private compileSvelteSsr(code: string, filename: string, sourcemap?: SourceMap): {
|
||||
private compileSvelteSsr(code: string, filename: string, canonicalViewName: string): {
|
||||
head: string,
|
||||
css: CssResult,
|
||||
html: string,
|
||||
} {
|
||||
logger.debug(canonicalViewName + ' > ', 'Compiling svelte ssr', filename);
|
||||
const svelteSsr = compile(code, {
|
||||
dev: false,
|
||||
dev: config.get<boolean>('view.dev'),
|
||||
generate: 'ssr',
|
||||
format: 'cjs',
|
||||
sourcemap: sourcemap,
|
||||
cssOutputFilename: filename + '.css',
|
||||
});
|
||||
|
||||
const locals = this.getGlobals();
|
||||
|
||||
return requireFromString(svelteSsr.js.code, filename).default.render({
|
||||
swaf: () => 'undefined',
|
||||
swaf: (key: string, args?: unknown[]) => {
|
||||
if (!args) return locals[key];
|
||||
|
||||
const f = locals[key];
|
||||
if (typeof f !== 'function') throw new Error(key + ' is not a function.');
|
||||
return f.call(locals, ...args);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private compileSvelteJS(code: string, sourcemap?: SourceMap): {
|
||||
code: string,
|
||||
map: SourceMap,
|
||||
} {
|
||||
const compiled = compile(code, {
|
||||
dev: false,
|
||||
hydratable: true,
|
||||
sourcemap: sourcemap,
|
||||
private async bundle(...canonicalViewNames: string[]): Promise<void> {
|
||||
logger.debug('Bundling');
|
||||
|
||||
const production = !config.get<boolean>('view.dev');
|
||||
const options: RollupOptions | RollupWatchOptions = {
|
||||
input: canonicalViewNames.map(name => path.join(this.svelteOutputPath, name)),
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'es',
|
||||
name: 'bundle',
|
||||
dir: path.join(this.publicAssetsPath, 'js'),
|
||||
entryFileNames: (chunkInfo: PreRenderedChunk): string => {
|
||||
const name = chunkInfo.facadeModuleId ?
|
||||
path.relative(this.svelteOutputPath, chunkInfo.facadeModuleId) :
|
||||
chunkInfo.name;
|
||||
return name + '.js';
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
preprocess: sveltePreprocess({
|
||||
typescript: {
|
||||
tsconfigFile: 'tsconfig.views.json',
|
||||
},
|
||||
}),
|
||||
compilerOptions: {
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
hydratable: true,
|
||||
},
|
||||
}),
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file - better for performance
|
||||
cssOnlyRollupPlugin({output: 'bundle.css'}),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration -
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
resolve({
|
||||
browser: true,
|
||||
dedupe: ['svelte'],
|
||||
}),
|
||||
commonjs(),
|
||||
],
|
||||
watch: {
|
||||
buildDelay: 1000,
|
||||
clearScreen: false,
|
||||
},
|
||||
};
|
||||
|
||||
if (production) {
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
options.plugins?.push(terser());
|
||||
} else {
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
const plugin = livereloadRollupPlugin('public');
|
||||
options.plugins?.push(plugin);
|
||||
}
|
||||
|
||||
for (const name of canonicalViewNames) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
fs.mkdir(path.dirname(path.join(this.publicAssetsPath, 'js', name)), {recursive: true}, err => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (production) {
|
||||
if (!this.rollup) {
|
||||
this.rollup = await rollup(options);
|
||||
|
||||
await this.rollup.write({
|
||||
format: 'es',
|
||||
dir: path.join(this.publicAssetsPath, 'js'),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (!this.rollup) {
|
||||
this.rollup = watch(options);
|
||||
this.rollup.on('event', (event) => {
|
||||
if (event.code === 'ERROR' || event.code === 'BUNDLE_END') {
|
||||
event.result?.close().catch(err => logger.error(err));
|
||||
logger.debug('Bundled from watch');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async resetBundle(): Promise<void> {
|
||||
if (this.rollup) {
|
||||
logger.debug('Stopping rollup...');
|
||||
await this.rollup.close();
|
||||
this.rollup = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async renderSvelte(
|
||||
file: string,
|
||||
locals: Record<string, unknown>,
|
||||
callback: (err: Error | null, rendered?: string) => void,
|
||||
): Promise<void> {
|
||||
const canonicalViewName = this.toCanonicalViewName(file);
|
||||
const actualFile = path.join(this.svelteOutputPath, canonicalViewName + COMPILED_SVELTE_EXTENSION);
|
||||
|
||||
if (!config.get<boolean>('view.enable_asset_cache')) delete this.fileCache[actualFile];
|
||||
const view = await this.getFileContentsFromCache(actualFile);
|
||||
|
||||
|
||||
const templateFile = await this.resolveViewFromCanonicalName('layouts/svelte_layout.html');
|
||||
|
||||
if (!config.get<boolean>('view.enable_asset_cache')) delete this.fileCache[templateFile];
|
||||
let output = await this.getFileContentsFromCache(templateFile);
|
||||
|
||||
|
||||
const [
|
||||
backendLines,
|
||||
head,
|
||||
html,
|
||||
css,
|
||||
] = view.split(FrontendToolsComponent.getSveltePreCompileSeparator(canonicalViewName));
|
||||
|
||||
locals = Object.assign(this.getGlobals(), locals);
|
||||
|
||||
const localMap: Record<string, unknown> = {};
|
||||
backendLines.split('\n').forEach(line => {
|
||||
const key = line.substring(1, line.indexOf(',') >= 0 ? line.indexOf(',') - 1 : line.length - 1);
|
||||
if (line.indexOf('[') >= 0) {
|
||||
const args = line.substring(line.indexOf('[') + 1, line.length - 1)
|
||||
.split(/, *?/)
|
||||
.map(arg => {
|
||||
if (arg.startsWith("'")) return '"' + arg.substring(1, arg.length - 1) + '"';
|
||||
return arg;
|
||||
})
|
||||
.map(arg => JSON.parse(arg));
|
||||
|
||||
const f = locals[key];
|
||||
if (typeof f !== 'function') throw new Error(key + ' is not a function.');
|
||||
localMap[`'${key}', ${JSON.stringify(args)}`] = f.call(locals, ...args);
|
||||
} else {
|
||||
localMap[`'${key}'`] = locals[key];
|
||||
}
|
||||
});
|
||||
const props = JSON.stringify(localMap);
|
||||
|
||||
output = output.replace('%canonicalViewName%', canonicalViewName);
|
||||
output = output.replace('%props%', props);
|
||||
output = output.replace('%head%', head);
|
||||
output = output.replace('%html%', html);
|
||||
output = output.replace('%css%', css);
|
||||
|
||||
callback(null, output);
|
||||
}
|
||||
|
||||
private async getFileContentsFromCache(file: string): Promise<string> {
|
||||
if (!this.fileCache[file]) {
|
||||
this.fileCache[file] = await new Promise<string>((resolve, reject) => {
|
||||
fs.readFile(file, (err, data) => {
|
||||
if (err) return reject(err);
|
||||
resolve(data.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return this.fileCache[file];
|
||||
}
|
||||
|
||||
private toCanonicalViewName(file: string): string {
|
||||
const resolvedFilePath = path.resolve(file);
|
||||
|
||||
let canonicalViewName: string | null = null;
|
||||
for (const viewPath of this.viewPaths) {
|
||||
if (resolvedFilePath.startsWith(viewPath)) {
|
||||
canonicalViewName = resolvedFilePath.substring(viewPath.length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!canonicalViewName) throw new Error('view ' + file + ' not found');
|
||||
return canonicalViewName;
|
||||
}
|
||||
|
||||
private async resolveViewFromCanonicalName(canonicalName: string): Promise<string> {
|
||||
for (const viewPath of this.viewPaths) {
|
||||
const tryPath = path.join(viewPath, canonicalName);
|
||||
if (await new Promise<boolean>((resolve, reject) => {
|
||||
fs.stat(tryPath, (err) => {
|
||||
if (err == null) {
|
||||
resolve(true);
|
||||
} else if (err.code === 'ENOENT') {
|
||||
resolve(false);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
})) {
|
||||
return tryPath;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('View not found from canonical name ' + canonicalName);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: add a way to add locals from anywhere
|
||||
*/
|
||||
private getGlobals(): Record<string, unknown> {
|
||||
return {
|
||||
code: compiled.js.code,
|
||||
map: compiled.js.map,
|
||||
route: (name: string) => 'unimplemented route ' + name,
|
||||
direct: 'access',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -48,8 +48,12 @@ export default class NunjucksComponent extends ApplicationComponent {
|
||||
.addFilter('hex', (v: number) => {
|
||||
return v.toString(16);
|
||||
});
|
||||
this.environment.express(app);
|
||||
app.set('view engine', 'njk');
|
||||
|
||||
app.engine('njk', (path, options, callback) => {
|
||||
this.environment?.render(path, options, (err, res) => {
|
||||
callback(err, res || undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async init(_router: Router): Promise<void> {
|
||||
|
8
src/types/RollupPluginCssOnly.d.ts
vendored
Normal file
8
src/types/RollupPluginCssOnly.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
declare module "rollup-plugin-css-only" {
|
||||
import {Plugin} from "rollup";
|
||||
export default function cssOnlyRollupPlugin(options: CssOnlyPluginOptions): Plugin;
|
||||
|
||||
export type CssOnlyPluginOptions = {
|
||||
output: string;
|
||||
};
|
||||
}
|
4
src/types/RollupPluginLivereload.d.ts
vendored
Normal file
4
src/types/RollupPluginLivereload.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module "rollup-plugin-livereload" {
|
||||
import {Plugin} from "rollup";
|
||||
export default function livereloadRollupPlugin(path: string): Plugin;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "public/js",
|
||||
"rootDir": "./views",
|
||||
"rootDir": "./build",
|
||||
"target": "ES6",
|
||||
"strict": true,
|
||||
"lib": [
|
||||
|
@ -1,29 +1,37 @@
|
||||
<script lang="ts">
|
||||
<script>
|
||||
import HomeDep from "./home_dep.svelte";
|
||||
import Layout from "./layout.svelte";
|
||||
|
||||
export let swaf = {};
|
||||
|
||||
let count: number;
|
||||
let count = 5;
|
||||
|
||||
function handleClick(): void {
|
||||
function handleClick() {
|
||||
count++;
|
||||
}
|
||||
let depTest;
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
p:last-of-type {
|
||||
color: blueviolet;
|
||||
}
|
||||
p:last-of-type {
|
||||
color: blueviolet;
|
||||
}
|
||||
|
||||
.style-test {
|
||||
p {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
.style-test {
|
||||
p {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<Layout title="Home"/>
|
||||
|
||||
<h1>Hello {count}!</h1>
|
||||
|
||||
<button on:click={handleClick}>More hellos!!</button>
|
||||
|
||||
<p>Direct access: {swaf.direct}</p>
|
||||
|
||||
{#if swaf.route('auth') === '/'}
|
||||
We're home!
|
||||
{:else}
|
||||
@ -32,10 +40,14 @@
|
||||
|
||||
<p>The route to auth is {swaf.route('auth')}</p>
|
||||
|
||||
<p>\$$.notcode</p>
|
||||
<p>\swaf.notcode</p>
|
||||
|
||||
<p>{`{\\$$.escaped}`}</p>
|
||||
<p>{`{\\swaf.escaped}`}</p>
|
||||
|
||||
<div class="style-test">
|
||||
<p>Blue!</p>
|
||||
</div>
|
||||
|
||||
<HomeDep swaf={swaf} bind:depTest={depTest}/>
|
||||
|
||||
<p>Dependency test: {depTest}</p>
|
||||
|
17
views/home_dep.svelte
Normal file
17
views/home_dep.svelte
Normal file
@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
export let depTest = 'Success';
|
||||
export let swaf = {};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
p {
|
||||
color: brown;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<p>Simple dep test</p>
|
||||
|
||||
<p>Nested swaf call: {swaf.direct}</p>
|
||||
<p>Nested swaf call: {swaf.route('auth')}</p>
|
||||
<p>Nested swaf call: {swaf.route('home')}</p>
|
23
views/layout.svelte
Normal file
23
views/layout.svelte
Normal file
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
export let title = undefined;
|
||||
export let description = undefined;
|
||||
export let refresh_after = undefined;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
|
||||
<title>{title || 'Undefined title'}</title>
|
||||
{#if description}
|
||||
<meta name="description" content={description}>
|
||||
{/if}
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="/img/logox1024.png">
|
||||
<link rel="shortcut icon" type="image/png" href="/img/logox128.png">
|
||||
<link rel="shortcut icon" type="image/svg" href="/img/logo.svg">
|
||||
|
||||
{#if refresh_after}
|
||||
<meta http-equiv="refresh" content={refresh_after}>
|
||||
{/if}
|
||||
</svelte:head>
|
29
views/layouts/svelte_layout.html
Normal file
29
views/layouts/svelte_layout.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
%head%
|
||||
|
||||
<style>%css%</style>
|
||||
<script type="module" defer>
|
||||
import View from '/js/%canonicalViewName%.js';
|
||||
|
||||
const props = %props%;
|
||||
|
||||
new View({
|
||||
hydrate: true,
|
||||
target: document.body,
|
||||
props: {
|
||||
swaf: (key, args) => {
|
||||
const line = args ?
|
||||
`'${key}', ${JSON.stringify(args)}`
|
||||
: `'${key}'`;
|
||||
return props[line];
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
%html%
|
||||
</body>
|
||||
</html>
|
220
yarn.lock
220
yarn.lock
@ -9,7 +9,7 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.10.4"
|
||||
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13":
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658"
|
||||
integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==
|
||||
@ -552,6 +552,48 @@
|
||||
resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4"
|
||||
integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==
|
||||
|
||||
"@rollup/plugin-commonjs@^17.1.0":
|
||||
version "17.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz#757ec88737dffa8aa913eb392fade2e45aef2a2d"
|
||||
integrity sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
commondir "^1.0.1"
|
||||
estree-walker "^2.0.1"
|
||||
glob "^7.1.6"
|
||||
is-reference "^1.2.1"
|
||||
magic-string "^0.25.7"
|
||||
resolve "^1.17.0"
|
||||
|
||||
"@rollup/plugin-node-resolve@^11.2.0":
|
||||
version "11.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz#82aa59397a29cd4e13248b106e6a4a1880362a60"
|
||||
integrity sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
"@types/resolve" "1.17.1"
|
||||
builtin-modules "^3.1.0"
|
||||
deepmerge "^4.2.2"
|
||||
is-module "^1.0.0"
|
||||
resolve "^1.19.0"
|
||||
|
||||
"@rollup/pluginutils@4":
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838"
|
||||
integrity sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==
|
||||
dependencies:
|
||||
estree-walker "^2.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@rollup/pluginutils@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
|
||||
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
|
||||
dependencies:
|
||||
"@types/estree" "0.0.39"
|
||||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@sindresorhus/is@^0.14.0":
|
||||
version "0.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
||||
@ -671,6 +713,16 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8"
|
||||
integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==
|
||||
|
||||
"@types/estree@*":
|
||||
version "0.0.47"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
|
||||
integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==
|
||||
|
||||
"@types/estree@0.0.39":
|
||||
version "0.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||
|
||||
"@types/express-serve-static-core@^4.17.18":
|
||||
version "4.17.19"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d"
|
||||
@ -853,6 +905,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/require-from-string/-/require-from-string-1.2.0.tgz#c18cfc8a2c1a0259e5841d1fef2b5e9d01c64242"
|
||||
integrity sha512-5vE9WoOOC9/DoD3Zj53UISpM+5tSvh8k0mL4fe2zFI6vO715/W4IQ3EdVUrWVMrFi1/NZhyMvm2iKsDFkEGddQ==
|
||||
|
||||
"@types/resolve@1.17.1":
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
|
||||
integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/sass@^1.16.0":
|
||||
version "1.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/sass/-/sass-1.16.0.tgz#b41ac1c17fa68ffb57d43e2360486ef526b3d57d"
|
||||
@ -1547,6 +1606,11 @@ buffer-from@1.x, buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
builtin-modules@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
|
||||
integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==
|
||||
|
||||
bytes@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||
@ -1727,7 +1791,7 @@ cheerio@^1.0.0-rc.3:
|
||||
parse5 "^6.0.1"
|
||||
parse5-htmlparser2-tree-adapter "^6.0.1"
|
||||
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.2.2, chokidar@^3.4.1, chokidar@^3.5.1:
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.2.2, chokidar@^3.4.1, chokidar@^3.5.0, chokidar@^3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
|
||||
integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
|
||||
@ -1887,6 +1951,11 @@ commander@^5.1.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
|
||||
|
||||
commondir@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||
|
||||
component-bind@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
|
||||
@ -2816,6 +2885,21 @@ estraverse@^5.1.0, estraverse@^5.2.0:
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
|
||||
integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
|
||||
|
||||
estree-walker@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
|
||||
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
|
||||
|
||||
estree-walker@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
|
||||
integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
|
||||
|
||||
estree-walker@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
@ -3861,6 +3945,11 @@ is-installed-globally@^0.3.1:
|
||||
global-dirs "^2.0.1"
|
||||
is-path-inside "^3.0.1"
|
||||
|
||||
is-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
||||
|
||||
is-negative-zero@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
|
||||
@ -3910,6 +3999,13 @@ is-potential-custom-element-name@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
|
||||
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
|
||||
|
||||
is-reference@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
|
||||
integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
|
||||
dependencies:
|
||||
"@types/estree" "*"
|
||||
|
||||
is-regex@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251"
|
||||
@ -4400,7 +4496,7 @@ jest-watcher@^26.6.2:
|
||||
jest-util "^26.6.2"
|
||||
string-length "^4.0.1"
|
||||
|
||||
jest-worker@^26.6.2:
|
||||
jest-worker@^26.2.1, jest-worker@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
|
||||
integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
|
||||
@ -4638,6 +4734,21 @@ lines-and-columns@^1.1.6:
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
||||
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
|
||||
|
||||
livereload-js@^3.3.1:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-3.3.2.tgz#c88b009c6e466b15b91faa26fd7c99d620e12651"
|
||||
integrity sha512-w677WnINxFkuixAoUEXOStewzLYGI76XVag+0JWMMEyjJQKs0ibWZMxkTlB96Lm3EjZ7IeOxVziBEbtxVQqQZA==
|
||||
|
||||
livereload@^0.9.1:
|
||||
version "0.9.3"
|
||||
resolved "https://registry.yarnpkg.com/livereload/-/livereload-0.9.3.tgz#a714816375ed52471408bede8b49b2ee6a0c55b1"
|
||||
integrity sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==
|
||||
dependencies:
|
||||
chokidar "^3.5.0"
|
||||
livereload-js "^3.3.1"
|
||||
opts ">= 1.2.0"
|
||||
ws "^7.4.3"
|
||||
|
||||
load-json-file@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
||||
@ -4740,6 +4851,13 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
magic-string@^0.25.7:
|
||||
version "0.25.7"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.4"
|
||||
|
||||
maildev@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/maildev/-/maildev-1.1.0.tgz#8b6977f244373be00112c942ae15dd32f5c225c9"
|
||||
@ -5705,6 +5823,11 @@ optionator@^0.9.1:
|
||||
type-check "^0.4.0"
|
||||
word-wrap "^1.2.3"
|
||||
|
||||
"opts@>= 1.2.0":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/opts/-/opts-2.0.2.tgz#a17e189fbbfee171da559edd8a42423bc5993ce1"
|
||||
integrity sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==
|
||||
|
||||
p-cancelable@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
|
||||
@ -5922,7 +6045,7 @@ performance-now@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d"
|
||||
integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==
|
||||
@ -6088,6 +6211,13 @@ random-bytes@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
|
||||
integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=
|
||||
|
||||
randombytes@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
range-parser@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
@ -6351,6 +6481,11 @@ require-main-filename@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||
|
||||
require-relative@^0.8.7:
|
||||
version "0.8.7"
|
||||
resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de"
|
||||
integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
@ -6373,7 +6508,7 @@ resolve-url@^0.2.1:
|
||||
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
|
||||
|
||||
resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1:
|
||||
resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
||||
@ -6412,6 +6547,52 @@ rimraf@^3.0.0, rimraf@^3.0.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rollup-plugin-css-only@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz#6a701cc5b051c6b3f0961e69b108a9a118e1b1df"
|
||||
integrity sha512-TYMOE5uoD76vpj+RTkQLzC9cQtbnJNktHPB507FzRWBVaofg7KhIqq1kGbcVOadARSozWF883Ho9KpSPKH8gqA==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "4"
|
||||
|
||||
rollup-plugin-livereload@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.0.tgz#d3928d74e8cf2ae4286c5dd46b770fd3f3b82313"
|
||||
integrity sha512-oC/8NqumGYuphkqrfszOHUUIwzKsaHBICw6QRwT5uD07gvePTS+HW+GFwu6f9K8W02CUuTvtIM9AWJrbj4wE1A==
|
||||
dependencies:
|
||||
livereload "^0.9.1"
|
||||
|
||||
rollup-plugin-svelte@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz#d45f2b92b1014be4eb46b55aa033fb9a9c65f04d"
|
||||
integrity sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==
|
||||
dependencies:
|
||||
require-relative "^0.8.7"
|
||||
rollup-pluginutils "^2.8.2"
|
||||
|
||||
rollup-plugin-terser@^7.0.2:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d"
|
||||
integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
jest-worker "^26.2.1"
|
||||
serialize-javascript "^4.0.0"
|
||||
terser "^5.0.0"
|
||||
|
||||
rollup-pluginutils@^2.8.2:
|
||||
version "2.8.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
|
||||
integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
|
||||
dependencies:
|
||||
estree-walker "^0.6.1"
|
||||
|
||||
rollup@^2.42.3:
|
||||
version "2.45.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.45.2.tgz#8fb85917c9f35605720e92328f3ccbfba6f78b48"
|
||||
integrity sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.1"
|
||||
|
||||
rsvp@^4.8.4:
|
||||
version "4.8.5"
|
||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||
@ -6441,7 +6622,7 @@ safe-buffer@5.2.0:
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
@ -6548,6 +6729,13 @@ send@0.17.1:
|
||||
range-parser "~1.2.1"
|
||||
statuses "~1.5.0"
|
||||
|
||||
serialize-javascript@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
|
||||
integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
serve-static@1.14.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
|
||||
@ -6753,7 +6941,7 @@ source-map-resolve@^0.5.0:
|
||||
source-map-url "^0.4.0"
|
||||
urix "^0.1.0"
|
||||
|
||||
source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6:
|
||||
source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.19:
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
||||
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
|
||||
@ -6783,11 +6971,16 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
source-map@^0.7.3:
|
||||
source-map@^0.7.3, source-map@~0.7.2:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
sourcemap-codec@^1.4.4:
|
||||
version "1.4.8"
|
||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||
|
||||
spawn-command@^0.0.2-1:
|
||||
version "0.0.2-1"
|
||||
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
|
||||
@ -7170,6 +7363,15 @@ terminal-link@^2.0.0:
|
||||
ansi-escapes "^4.2.1"
|
||||
supports-hyperlinks "^2.0.0"
|
||||
|
||||
terser@^5.0.0:
|
||||
version "5.6.1"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.6.1.tgz#a48eeac5300c0a09b36854bf90d9c26fb201973c"
|
||||
integrity sha512-yv9YLFQQ+3ZqgWCUk+pvNJwgUTdlIxUk1WTN+RnaFJe2L7ipG2csPT0ra2XRm7Cs8cxN7QXmK1rFzEwYEQkzXw==
|
||||
dependencies:
|
||||
commander "^2.20.0"
|
||||
source-map "~0.7.2"
|
||||
source-map-support "~0.5.19"
|
||||
|
||||
test-exclude@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
|
||||
@ -7763,7 +7965,7 @@ write-file-atomic@^3.0.0:
|
||||
signal-exit "^3.0.2"
|
||||
typedarray-to-buffer "^3.1.5"
|
||||
|
||||
ws@^7.2.3, ws@^7.4.4:
|
||||
ws@^7.2.3, ws@^7.4.3, ws@^7.4.4:
|
||||
version "7.4.5"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1"
|
||||
integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==
|
||||
|
Loading…
Reference in New Issue
Block a user