Merge remote-tracking branch 'boilerplate/develop' into develop
# Conflicts: # assets/config.json # assets/sass/_vars.scss # assets/sass/app.scss # assets/sass/layout.scss # assets/ts/copyable_text.ts # package.json # src/App.ts # tsconfig.json # views/about.njk # views/layouts/base.njk
This commit is contained in:
commit
17c7674c3e
135
.eslintrc.cjs
Normal file
135
.eslintrc.cjs
Normal file
@ -0,0 +1,135 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [
|
||||
'svelte3',
|
||||
'@typescript-eslint',
|
||||
'import',
|
||||
'simple-import-sort',
|
||||
],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: [
|
||||
'./tsconfig.test.json',
|
||||
'./src/tsconfig.json',
|
||||
'./src/common/tsconfig.json',
|
||||
'./src/assets/ts/tsconfig.eslint.json',
|
||||
'./src/assets/views/tsconfig.json',
|
||||
]
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
rules: {
|
||||
indent: [
|
||||
'error',
|
||||
4,
|
||||
{
|
||||
SwitchCase: 1
|
||||
}
|
||||
],
|
||||
'no-trailing-spaces': 'error',
|
||||
'max-len': [
|
||||
'error',
|
||||
{
|
||||
code: 120,
|
||||
ignoreTemplateLiterals: true,
|
||||
ignoreRegExpLiterals: true
|
||||
}
|
||||
],
|
||||
semi: 'off',
|
||||
'@typescript-eslint/semi': [
|
||||
'error'
|
||||
],
|
||||
'no-extra-semi': 'error',
|
||||
'eol-last': 'error',
|
||||
'comma-dangle': 'off',
|
||||
'simple-import-sort/imports': 'error',
|
||||
'no-extra-parens': 'off',
|
||||
'no-nested-ternary': 'error',
|
||||
'no-return-await': 'off',
|
||||
'no-useless-return': 'error',
|
||||
'no-useless-constructor': 'off',
|
||||
'import/extensions': ['error', 'ignorePackages'],
|
||||
'@typescript-eslint/comma-dangle': [
|
||||
'error',
|
||||
{
|
||||
arrays: 'always-multiline',
|
||||
objects: 'always-multiline',
|
||||
imports: 'always-multiline',
|
||||
exports: 'always-multiline',
|
||||
functions: 'always-multiline',
|
||||
enums: 'always-multiline',
|
||||
generics: 'always-multiline',
|
||||
tuples: 'always-multiline'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-extra-parens': [
|
||||
'error'
|
||||
],
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'error',
|
||||
'@typescript-eslint/no-unnecessary-condition': 'error',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
||||
'@typescript-eslint/no-useless-constructor': [
|
||||
'error'
|
||||
],
|
||||
'@typescript-eslint/return-await': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@typescript-eslint/explicit-member-accessibility': [
|
||||
'error',
|
||||
{
|
||||
accessibility: 'explicit'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
},
|
||||
ignorePatterns: [
|
||||
'.eslintrc.js',
|
||||
'rollup.config.js',
|
||||
'jest.config.js',
|
||||
'dist/**/*',
|
||||
'config/**/*',
|
||||
'intermediates/**/*',
|
||||
'public/**/*',
|
||||
'scripts/**/*',
|
||||
'src/frontend/register_svelte/register_svelte.js',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'test/**/*'
|
||||
],
|
||||
rules: {
|
||||
'max-len': [
|
||||
'error',
|
||||
{
|
||||
code: 120,
|
||||
ignoreTemplateLiterals: true,
|
||||
ignoreRegExpLiterals: true,
|
||||
ignoreStrings: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
processor: 'svelte3/svelte3'
|
||||
}
|
||||
],
|
||||
settings: {
|
||||
'svelte3/typescript': require('typescript'),
|
||||
'svelte3/ignore-styles': function (attributes) {
|
||||
return !!(attributes['lang'] && attributes['lang'] !== 'css');
|
||||
}
|
||||
},
|
||||
}
|
115
.eslintrc.json
115
.eslintrc.json
@ -1,115 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"./tsconfig.json",
|
||||
"./tsconfig.test.json",
|
||||
"./tsconfig.frontend.json"
|
||||
]
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
4,
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"no-trailing-spaces": "error",
|
||||
"max-len": [
|
||||
"error",
|
||||
{
|
||||
"code": 120,
|
||||
"ignoreStrings": true,
|
||||
"ignoreTemplateLiterals": true,
|
||||
"ignoreRegExpLiterals": true
|
||||
}
|
||||
],
|
||||
"semi": "off",
|
||||
"@typescript-eslint/semi": [
|
||||
"error"
|
||||
],
|
||||
"no-extra-semi": "error",
|
||||
"eol-last": "error",
|
||||
"comma-dangle": "off",
|
||||
"@typescript-eslint/comma-dangle": [
|
||||
"error",
|
||||
{
|
||||
"arrays": "always-multiline",
|
||||
"objects": "always-multiline",
|
||||
"imports": "always-multiline",
|
||||
"exports": "always-multiline",
|
||||
"functions": "always-multiline",
|
||||
"enums": "always-multiline",
|
||||
"generics": "always-multiline",
|
||||
"tuples": "always-multiline"
|
||||
}
|
||||
],
|
||||
"no-extra-parens": "off",
|
||||
"@typescript-eslint/no-extra-parens": [
|
||||
"error"
|
||||
],
|
||||
"no-nested-ternary": "error",
|
||||
"@typescript-eslint/no-inferrable-types": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "error",
|
||||
"@typescript-eslint/no-unnecessary-condition": "error",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"no-useless-return": "error",
|
||||
"no-useless-constructor": "off",
|
||||
"@typescript-eslint/no-useless-constructor": [
|
||||
"error"
|
||||
],
|
||||
"no-return-await": "off",
|
||||
"@typescript-eslint/return-await": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"error",
|
||||
{
|
||||
"accessibility": "explicit"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-floating-promises": "error"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"jest.config.js",
|
||||
"scripts/**/*",
|
||||
"webpack.config.js",
|
||||
"dist/**/*",
|
||||
"public/**/*",
|
||||
"config/**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"test/**/*"
|
||||
],
|
||||
"rules": {
|
||||
"max-len": [
|
||||
"error",
|
||||
{
|
||||
"code": 120,
|
||||
"ignoreTemplateLiterals": true,
|
||||
"ignoreRegExpLiterals": true,
|
||||
"ignoreStrings": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -8,3 +8,6 @@ storage/uploads
|
||||
|
||||
config/local.*
|
||||
src/package.json
|
||||
|
||||
intermediates/
|
||||
dist/
|
@ -1,81 +0,0 @@
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Light'), local('NunitoSans-Light'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe03MImSLYBIv1o4X1M8cc8WAc5iU1EQVg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Light'), local('NunitoSans-Light'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe03MImSLYBIv1o4X1M8cc8WAc5jU1EQVg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Light'), local('NunitoSans-Light'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe03MImSLYBIv1o4X1M8cc8WAc5tU1E.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Regular'), local('NunitoSans-Regular'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe0qMImSLYBIv1o4X1M8cceyI9tScg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Regular'), local('NunitoSans-Regular'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe0qMImSLYBIv1o4X1M8ccezI9tScg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Regular'), local('NunitoSans-Regular'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe0qMImSLYBIv1o4X1M8cce9I9s.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Bold'), local('NunitoSans-Bold'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe03MImSLYBIv1o4X1M8cc8GBs5iU1EQVg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Bold'), local('NunitoSans-Bold'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe03MImSLYBIv1o4X1M8cc8GBs5jU1EQVg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local('Nunito Sans Bold'), local('NunitoSans-Bold'), url(https://fonts.gstatic.com/s/nunitosans/v5/pe03MImSLYBIv1o4X1M8cc8GBs5tU1E.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
@import "layout";
|
||||
|
||||
header, footer {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.messages {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.error-instructions {
|
||||
margin-top: 32px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "Oops";
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
|
||||
font-size: #{'min(50vh, 40vw)'};
|
||||
opacity: 0.025;
|
||||
}
|
||||
}
|
||||
|
||||
.contact {
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
|
||||
color: $defaultTextColor;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
|
||||
&::before {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
background-image: url(../img/logo.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 64px;
|
||||
|
||||
opacity: 0.075;
|
||||
filter: contrast(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
@import "vars";
|
||||
|
||||
@mixin container {
|
||||
width: 100%;
|
||||
padding: 0 8px;
|
||||
|
||||
@media (min-width: $mobileThreshold) {
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
@media (min-width: $desktopThreshold) {
|
||||
width: $desktopThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
@include container;
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
export default class PersistentWebsocket {
|
||||
private webSocket?: WebSocket;
|
||||
|
||||
public constructor(
|
||||
protected readonly url: string,
|
||||
private readonly handler: MessageHandler,
|
||||
protected readonly reconnectOnClose: boolean = true,
|
||||
) {
|
||||
}
|
||||
|
||||
public run(): void {
|
||||
const _webSocket = this.webSocket = new WebSocket(this.url);
|
||||
this.webSocket.addEventListener('open', () => {
|
||||
console.debug('Websocket connected');
|
||||
});
|
||||
this.webSocket.addEventListener('message', (e) => {
|
||||
this.handler(_webSocket, e);
|
||||
});
|
||||
this.webSocket.addEventListener('error', (e) => {
|
||||
console.error('Websocket error', e);
|
||||
});
|
||||
this.webSocket.addEventListener('close', (e) => {
|
||||
this.webSocket = undefined;
|
||||
console.debug('Websocket closed', e.code, e.reason);
|
||||
|
||||
if (this.reconnectOnClose) {
|
||||
setTimeout(() => this.run(), 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public send(data: string): void {
|
||||
if (!this.webSocket) throw new Error('WebSocket not connected');
|
||||
|
||||
this.webSocket.send(data);
|
||||
}
|
||||
}
|
||||
|
||||
export type MessageHandler = (webSocket: WebSocket, e: MessageEvent) => void;
|
@ -1,10 +0,0 @@
|
||||
import './external_links';
|
||||
import './message_icons';
|
||||
import './forms';
|
||||
import './copyable_text';
|
||||
import './tooltips-and-dropdowns';
|
||||
import './main_menu';
|
||||
import './font-awesome';
|
||||
|
||||
// css
|
||||
import '../sass/app.scss';
|
@ -1,11 +0,0 @@
|
||||
import feather from "feather-icons";
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('a[target="_blank"]').forEach(el => {
|
||||
if (!el.classList.contains('no-icon')) {
|
||||
el.innerHTML += `<i data-feather="external-link"></i>`;
|
||||
}
|
||||
});
|
||||
|
||||
feather.replace();
|
||||
});
|
@ -1,4 +0,0 @@
|
||||
import '../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss';
|
||||
import '../../node_modules/@fortawesome/fontawesome-free/scss/regular.scss';
|
||||
import '../../node_modules/@fortawesome/fontawesome-free/scss/solid.scss';
|
||||
import '../../node_modules/@fortawesome/fontawesome-free/scss/brands.scss';
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* For labels to update their state (css selectors based on the value attribute)
|
||||
*/
|
||||
import {ValidationError} from "swaf/db/Validator";
|
||||
|
||||
export function updateInputs(): void {
|
||||
document.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>('input, textarea').forEach(el => {
|
||||
if (!el.dataset.inputSetup) {
|
||||
el.dataset.inputSetup = 'true';
|
||||
if (el.type !== 'checkbox') {
|
||||
el.setAttribute('value', el.value);
|
||||
el.addEventListener('change', () => {
|
||||
el.setAttribute('value', el.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
updateInputs();
|
||||
});
|
||||
|
||||
export function applyFormMessages(
|
||||
formElement: HTMLFormElement,
|
||||
messages: { [p: string]: ValidationError<unknown> },
|
||||
): void {
|
||||
for (const fieldName of Object.keys(messages)) {
|
||||
const field = formElement.querySelector('#field-' + fieldName);
|
||||
if (!field) continue;
|
||||
|
||||
let parent = field.parentElement;
|
||||
while (parent && !parent.classList.contains('form-field')) parent = parent.parentElement;
|
||||
|
||||
let err = field.querySelector('.error');
|
||||
if (!err) {
|
||||
err = document.createElement('div');
|
||||
err.classList.add('error');
|
||||
parent?.insertBefore(err, parent.querySelector('.hint') || parent);
|
||||
}
|
||||
err.innerHTML = `<i data-feather="x-circle"></i> ${messages[fieldName].message}`;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const menuButton = document.getElementById('menu-button');
|
||||
const mainMenu = document.getElementById('main-menu');
|
||||
|
||||
if (menuButton) {
|
||||
menuButton.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
mainMenu?.classList.toggle('open');
|
||||
});
|
||||
}
|
||||
|
||||
if (mainMenu) {
|
||||
mainMenu.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
document.addEventListener('click', () => {
|
||||
mainMenu.classList.remove('open');
|
||||
});
|
||||
}
|
||||
});
|
@ -1,26 +0,0 @@
|
||||
import feather from "feather-icons";
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const messageTypeToIcon: { [p: string]: string } = {
|
||||
info: 'info',
|
||||
success: 'check',
|
||||
warning: 'alert-triangle',
|
||||
error: 'x-circle',
|
||||
question: 'help-circle',
|
||||
};
|
||||
|
||||
document.querySelectorAll<HTMLElement>('.message').forEach(el => {
|
||||
const icon = el.querySelector('.icon');
|
||||
const type = el.dataset['type'];
|
||||
if (!icon || !type) return;
|
||||
if (!messageTypeToIcon[type]) throw new Error(`No icon for type ${type}`);
|
||||
|
||||
const svgContainer = document.createElement('div');
|
||||
svgContainer.innerHTML = feather.icons[messageTypeToIcon[type]].toSvg();
|
||||
|
||||
if (svgContainer.firstChild) el.insertBefore(svgContainer.firstChild, icon);
|
||||
icon.remove();
|
||||
});
|
||||
|
||||
feather.replace();
|
||||
});
|
@ -1,31 +0,0 @@
|
||||
export function updateTooltips(): void {
|
||||
console.debug('Updating tooltips');
|
||||
const elements = document.querySelectorAll<HTMLElement>('.tip, .dropdown');
|
||||
|
||||
// Calculate max potential displacement
|
||||
let max = 0;
|
||||
elements.forEach(el => {
|
||||
const box = el.getBoundingClientRect();
|
||||
if (max < box.height) max = box.height;
|
||||
});
|
||||
|
||||
// Prevent displacement
|
||||
elements.forEach(el => {
|
||||
if (!el.dataset.tooltipSetup) {
|
||||
el.dataset.tooltipSetup = 'true';
|
||||
const box = el.getBoundingClientRect();
|
||||
if (box.bottom >= document.body.clientHeight - (max + 32)) {
|
||||
el.classList.add('top');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.addEventListener('popstate', () => {
|
||||
updateTooltips();
|
||||
});
|
||||
window.requestAnimationFrame(() => {
|
||||
updateTooltips();
|
||||
});
|
||||
});
|
64
package.json
64
package.json
@ -10,58 +10,52 @@
|
||||
"test": "jest --verbose --runInBand",
|
||||
"clean": "node scripts/clean.js",
|
||||
"prepare-sources": "node scripts/prepare-sources.js",
|
||||
"compile": "yarn clean && tsc",
|
||||
"build": "yarn prepare-sources && yarn compile && webpack --mode production",
|
||||
"dev": "yarn prepare-sources && concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"",
|
||||
"start": "yarn build && node",
|
||||
"lint": "eslint ."
|
||||
"compile": "yarn clean && yarn prepare-sources && tsc --build",
|
||||
"build": "yarn compile && node . pre-compile-views && node scripts/dist.js",
|
||||
"build-production": "NODE_ENV=production yarn build",
|
||||
"dev": "yarn compile && concurrently -k -n \"Maildev,Typescript,ViewPreCompile,Node\" -p \"[{name}]\" -c \"yellow,blue,red,green\" \"maildev\" \"tsc --build --watch --preserveWatchOutput\" \"nodemon -i public -i intermediates -- pre-compile-views --watch\" \"nodemon -i public -i intermediates\"",
|
||||
"lint": "eslint .",
|
||||
"start": "yarn build-production && node ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.5",
|
||||
"@fortawesome/fontawesome-free": "^5.14.0",
|
||||
"@types/config": "^0.0.38",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"@types/config": "^0.0.40",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-session": "^1.17.0",
|
||||
"@types/feather-icons": "^4.7.0",
|
||||
"@types/formidable": "^1.0.31",
|
||||
"@types/jest": "^26.0.4",
|
||||
"@types/formidable": "^2.0.0",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@types/mysql": "^2.15.15",
|
||||
"@types/node": "^14.6.3",
|
||||
"@types/node": "^17.0.4",
|
||||
"@types/nodemailer": "^6.4.0",
|
||||
"@types/nunjucks": "^3.1.3",
|
||||
"@types/ws": "^7.2.6",
|
||||
"@typescript-eslint/eslint-plugin": "^4.3.0",
|
||||
"@typescript-eslint/parser": "^4.3.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"@types/ws": "^8.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"concurrently": "^6.0.0",
|
||||
"css-loader": "^5.0.0",
|
||||
"eslint": "^7.10.0",
|
||||
"eslint": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"eslint-plugin-svelte3": "^3.2.1",
|
||||
"feather-icons": "^4.28.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"imagemin": "^7.0.0",
|
||||
"imagemin-gifsicle": "^7.0.0",
|
||||
"imagemin-mozjpeg": "^9.0.0",
|
||||
"imagemin-pngquant": "^9.0.0",
|
||||
"imagemin-svgo": "^9.0.0",
|
||||
"img-loader": "^3.0.1",
|
||||
"jest": "^26.1.0",
|
||||
"imagemin-mozjpeg": "^10.0.0",
|
||||
"imagemin-pngquant": "^9.0.2",
|
||||
"imagemin-svgo": "^10.0.0",
|
||||
"imagemin-webp": "^7.0.0",
|
||||
"jest": "^27.0.4",
|
||||
"maildev": "^1.1.0",
|
||||
"mini-css-extract-plugin": "^1.2.1",
|
||||
"node-sass": "^5.0.0",
|
||||
"nodemon": "^2.0.3",
|
||||
"sass-loader": "^11.0.1",
|
||||
"terser-webpack-plugin": "^5.0.3",
|
||||
"ts-jest": "^26.1.1",
|
||||
"ts-loader": "^9.1.0",
|
||||
"typescript": "^4.0.2",
|
||||
"webpack": "^5.3.2",
|
||||
"webpack-cli": "^4.1.0"
|
||||
"sass": "^1.32.12",
|
||||
"svelte": "^3.44.2",
|
||||
"svgo": "^2.3.0",
|
||||
"ts-jest": "^27.0.3",
|
||||
"typescript": "^4.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"config": "^3.3.1",
|
||||
"express": "^4.17.1",
|
||||
"formidable": "^1.2.2",
|
||||
"swaf": "^0.23.0"
|
||||
"swaf": "^0.24.7"
|
||||
}
|
||||
}
|
||||
|
22
scripts/_functions.js
Normal file
22
scripts/_functions.js
Normal file
@ -0,0 +1,22 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function copyRecursively(file, destination) {
|
||||
const target = path.join(destination, path.basename(file));
|
||||
if (fs.statSync(file).isDirectory()) {
|
||||
console.log('mkdir', target);
|
||||
|
||||
fs.mkdirSync(target, {recursive: true});
|
||||
fs.readdirSync(file).forEach(f => {
|
||||
copyRecursively(path.join(file, f), target);
|
||||
});
|
||||
} else {
|
||||
console.log('> cp ', target);
|
||||
|
||||
fs.copyFileSync(file, target);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
copyRecursively,
|
||||
};
|
@ -1,7 +1,9 @@
|
||||
const fs = require('fs');
|
||||
|
||||
[
|
||||
'intermediates',
|
||||
'dist',
|
||||
'public',
|
||||
].forEach(file => {
|
||||
if (fs.existsSync(file)) {
|
||||
console.log('Cleaning', file, '...');
|
||||
|
22
scripts/dist.js
Normal file
22
scripts/dist.js
Normal file
@ -0,0 +1,22 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {copyRecursively} = require('./_functions.js');
|
||||
|
||||
|
||||
[
|
||||
'yarn.lock',
|
||||
'README.md',
|
||||
'config/',
|
||||
].forEach(file => {
|
||||
copyRecursively(file, 'dist');
|
||||
});
|
||||
|
||||
fs.mkdirSync('dist/types', {recursive: true});
|
||||
|
||||
fs.readdirSync('src/types').forEach(file => {
|
||||
copyRecursively(path.join('src/types', file), 'dist/types');
|
||||
});
|
||||
|
||||
fs.readdirSync('src/assets').forEach(file => {
|
||||
copyRecursively(path.join('src/assets', file), 'dist/assets');
|
||||
});
|
@ -1,4 +1,28 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
fs.copyFileSync('package.json', path.join('src', 'package.json'));
|
||||
// These folders must exist for nodemon not to loop indefinitely.
|
||||
[
|
||||
'public',
|
||||
'dist',
|
||||
'intermediates',
|
||||
'intermediates/assets',
|
||||
].forEach(dir => {
|
||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
|
||||
});
|
||||
|
||||
// Symlink to build/common
|
||||
const commonLocalSymlink = path.resolve('intermediates/common-local');
|
||||
if (!fs.existsSync(commonLocalSymlink)) {
|
||||
const target = path.resolve('dist/common-local');
|
||||
fs.symlinkSync(target, commonLocalSymlink);
|
||||
}
|
||||
|
||||
const commonSymlink = path.resolve('intermediates/common');
|
||||
if (!fs.existsSync(commonSymlink)) {
|
||||
const target = path.resolve('node_modules/swaf/common');
|
||||
fs.symlinkSync(target, commonSymlink);
|
||||
}
|
||||
|
||||
// Copy package.json
|
||||
fs.copyFileSync('package.json', 'dist/package.json');
|
||||
|
82
src/App.ts
82
src/App.ts
@ -1,57 +1,39 @@
|
||||
import Application from "swaf/Application";
|
||||
import Migration, {MigrationType} from "swaf/db/Migration";
|
||||
import CreateMigrationsTable from "swaf/migrations/CreateMigrationsTable";
|
||||
import AutoUpdateComponent from "swaf/components/AutoUpdateComponent";
|
||||
import CsrfProtectionComponent from "swaf/components/CsrfProtectionComponent";
|
||||
import ExpressAppComponent from "swaf/components/ExpressAppComponent";
|
||||
import NunjucksComponent from "swaf/components/NunjucksComponent";
|
||||
import MysqlComponent from "swaf/components/MysqlComponent";
|
||||
import FormHelperComponent from "swaf/components/FormHelperComponent";
|
||||
import FrontendToolsComponent from "swaf/components/FrontendToolsComponent";
|
||||
import LogRequestsComponent from "swaf/components/LogRequestsComponent";
|
||||
import MailComponent from "swaf/components/MailComponent";
|
||||
import MaintenanceComponent from "swaf/components/MaintenanceComponent";
|
||||
import MysqlComponent from "swaf/components/MysqlComponent";
|
||||
import PreviousUrlComponent from "swaf/components/PreviousUrlComponent";
|
||||
import RedisComponent from "swaf/components/RedisComponent";
|
||||
import ServeStaticDirectoryComponent from "swaf/components/ServeStaticDirectoryComponent";
|
||||
import MaintenanceComponent from "swaf/components/MaintenanceComponent";
|
||||
import MailComponent from "swaf/components/MailComponent";
|
||||
import SessionComponent from "swaf/components/SessionComponent";
|
||||
import FormHelperComponent from "swaf/components/FormHelperComponent";
|
||||
import CsrfProtectionComponent from "swaf/components/CsrfProtectionComponent";
|
||||
import WebSocketServerComponent from "swaf/components/WebSocketServerComponent";
|
||||
import AboutController from "./controllers/AboutController";
|
||||
import AutoUpdateComponent from "swaf/components/AutoUpdateComponent";
|
||||
import MagicLinkWebSocketListener from "swaf/auth/magic_link/MagicLinkWebSocketListener";
|
||||
import FileController from "./controllers/FileController";
|
||||
import CreateAuthTokensTable from "./migrations/CreateAuthTokensTable";
|
||||
import AuthComponent from "swaf/auth/AuthComponent";
|
||||
import CreateFilesTable from "./migrations/CreateFilesTable";
|
||||
import IncreaseFilesSizeField from "./migrations/IncreaseFilesSizeField";
|
||||
import CreateUrlRedirectsTable from "./migrations/CreateUrlRedirectsTable";
|
||||
import AuthTokenController from "./controllers/AuthTokenController";
|
||||
import URLRedirectController from "./controllers/URLRedirectController";
|
||||
import LinkController from "./controllers/LinkController";
|
||||
import BackendController from "swaf/helpers/BackendController";
|
||||
import DummyMigration from "swaf/migrations/DummyMigration";
|
||||
import Migration, {MigrationType} from "swaf/db/Migration";
|
||||
import AssetCompiler from "swaf/frontend/AssetCompiler";
|
||||
import CopyAssetPreCompiler from "swaf/frontend/CopyAssetPreCompiler";
|
||||
import MailViewEngine from "swaf/frontend/MailViewEngine";
|
||||
import NunjucksViewEngine from "swaf/frontend/NunjucksViewEngine";
|
||||
import ScssAssetPreCompiler from "swaf/frontend/ScssAssetPreCompiler";
|
||||
import SvelteViewEngine from "swaf/frontend/SvelteViewEngine";
|
||||
import TypeScriptPreCompiler from "swaf/frontend/TypeScriptPreCompiler";
|
||||
import CreateMigrationsTable from "swaf/migrations/CreateMigrationsTable";
|
||||
import DropLegacyLogsTable from "swaf/migrations/DropLegacyLogsTable";
|
||||
import CreateUsersAndUserEmailsTableMigration from "swaf/auth/migrations/CreateUsersAndUserEmailsTableMigration";
|
||||
import CreateMagicLinksTableMigration from "swaf/auth/magic_link/CreateMagicLinksTableMigration";
|
||||
import AddApprovedFieldToUsersTableMigration from "swaf/auth/migrations/AddApprovedFieldToUsersTableMigration";
|
||||
import PreviousUrlComponent from "swaf/components/PreviousUrlComponent";
|
||||
import MagicLinkAuthMethod from "swaf/auth/magic_link/MagicLinkAuthMethod";
|
||||
import {MAGIC_LINK_MAIL} from "swaf/Mails";
|
||||
import PasswordAuthMethod from "swaf/auth/password/PasswordAuthMethod";
|
||||
import MailController from "swaf/mail/MailController";
|
||||
import AccountController from "swaf/auth/AccountController";
|
||||
import AuthController from "swaf/auth/AuthController";
|
||||
import MagicLinkController from "swaf/auth/magic_link/MagicLinkController";
|
||||
import AddUsedToMagicLinksMigration from "swaf/auth/magic_link/AddUsedToMagicLinksMigration";
|
||||
import MakeMagicLinksSessionNotUniqueMigration from "swaf/auth/magic_link/MakeMagicLinksSessionNotUniqueMigration";
|
||||
import AddPasswordToUsersMigration from "swaf/auth/password/AddPasswordToUsersMigration";
|
||||
import DeleteOldFilesJobComponent from "./DeleteOldFilesJobComponent";
|
||||
import packageJson = require('./package.json');
|
||||
import ReplaceTtlWithExpiresAtFilesTable from "./migrations/ReplaceTtlWithExpiresAtFilesTable";
|
||||
import DummyMigration from "swaf/migrations/DummyMigration";
|
||||
|
||||
import HomeController from "./controllers/HomeController.js";
|
||||
|
||||
export default class App extends Application {
|
||||
public constructor(
|
||||
version: string,
|
||||
private readonly addr: string,
|
||||
private readonly port: number,
|
||||
) {
|
||||
super(packageJson.version);
|
||||
super(version);
|
||||
}
|
||||
|
||||
protected getMigrations(): MigrationType<Migration>[] {
|
||||
@ -91,18 +73,27 @@ export default class App extends Application {
|
||||
this.use(new ServeStaticDirectoryComponent('node_modules/feather-icons/dist', '/icons'));
|
||||
|
||||
// Dynamic views and routes
|
||||
this.use(new NunjucksComponent());
|
||||
const intermediateDirectory = 'intermediates/assets';
|
||||
const assetCompiler = new AssetCompiler(intermediateDirectory, 'public');
|
||||
const additionalViewPaths = ['test/assets'];
|
||||
this.use(new FrontendToolsComponent(
|
||||
assetCompiler,
|
||||
new CopyAssetPreCompiler(intermediateDirectory, '', 'json', additionalViewPaths, false),
|
||||
new ScssAssetPreCompiler(intermediateDirectory, assetCompiler.targetDir, 'scss', additionalViewPaths),
|
||||
new CopyAssetPreCompiler(intermediateDirectory, 'img', 'svg', additionalViewPaths, true),
|
||||
new TypeScriptPreCompiler(intermediateDirectory, additionalViewPaths),
|
||||
new SvelteViewEngine(intermediateDirectory, ...additionalViewPaths),
|
||||
new NunjucksViewEngine(intermediateDirectory, ...additionalViewPaths),
|
||||
));
|
||||
this.use(new PreviousUrlComponent());
|
||||
|
||||
// Maintenance
|
||||
this.use(new MaintenanceComponent(this, () => {
|
||||
return this.as(RedisComponent).canServe() && this.as(MysqlComponent).canServe();
|
||||
}));
|
||||
this.use(new MaintenanceComponent());
|
||||
this.use(new AutoUpdateComponent());
|
||||
|
||||
// Services
|
||||
this.use(new MysqlComponent());
|
||||
this.use(new MailComponent());
|
||||
this.use(new MailComponent(new MailViewEngine(intermediateDirectory, ...additionalViewPaths)));
|
||||
|
||||
// Session
|
||||
this.use(new RedisComponent());
|
||||
@ -117,6 +108,7 @@ export default class App extends Application {
|
||||
|
||||
// WebSocket server
|
||||
this.use(new WebSocketServerComponent(this, this.as(ExpressAppComponent), this.as(RedisComponent)));
|
||||
this.use(new WebSocketServerComponent());
|
||||
|
||||
|
||||
// Jobs
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
3
src/assets/package.json
Normal file
3
src/assets/package.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
6
src/assets/ts/tsconfig.eslint.json
Normal file
6
src/assets/ts/tsconfig.eslint.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"./**/*"
|
||||
]
|
||||
}
|
27
src/assets/ts/tsconfig.json
Normal file
27
src/assets/ts/tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "CommonJS",
|
||||
"baseUrl": "../../../intermediates/assets",
|
||||
"rootDir": "../../../intermediates/assets/ts-source",
|
||||
"sourceRoot": "../../../intermediates/assets/ts-source",
|
||||
"outDir": "../../../intermediates/assets/ts",
|
||||
"declaration": false,
|
||||
|
||||
"typeRoots": [],
|
||||
"resolveJsonModule": false,
|
||||
"lib": [
|
||||
"es2020",
|
||||
"DOM"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"../../../intermediates/assets/ts-source/**/*"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../common"
|
||||
}
|
||||
]
|
||||
}
|
15
src/assets/views/tsconfig.json
Normal file
15
src/assets/views/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "public/js",
|
||||
"rootDir": "../../../intermediates/assets",
|
||||
},
|
||||
"include": [
|
||||
"src/assets/ts/**/*"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../common"
|
||||
}
|
||||
]
|
||||
}
|
1
src/common/dummy.ts
Normal file
1
src/common/dummy.ts
Normal file
@ -0,0 +1 @@
|
||||
console.log('common code between back and front');
|
3
src/common/package.json
Normal file
3
src/common/package.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
20
src/common/tsconfig.json
Normal file
20
src/common/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
|
||||
"module": "CommonJS",
|
||||
|
||||
"baseUrl": "../../dist/common-local",
|
||||
"rootDir": "./",
|
||||
"sourceRoot": "./",
|
||||
"outDir": "../../dist/common-local",
|
||||
|
||||
"typeRoots": [
|
||||
"src/types"
|
||||
],
|
||||
},
|
||||
"include": [
|
||||
"./**/*"
|
||||
]
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import Controller from "swaf/Controller";
|
||||
import {Request, Response} from "express";
|
||||
import {route} from "swaf/common/Routing";
|
||||
import Controller from "swaf/Controller";
|
||||
|
||||
export default class HomeController extends Controller {
|
||||
public routes(): void {
|
||||
@ -20,6 +21,6 @@ export default class HomeController extends Controller {
|
||||
* This is to test and assert that swaf extended types are available
|
||||
*/
|
||||
protected async goBack(req: Request, res: Response): Promise<void> {
|
||||
res.redirect(req.getPreviousUrl() || Controller.route('home'));
|
||||
res.redirect(req.getPreviousUrl() || route('home'));
|
||||
}
|
||||
}
|
||||
|
14
src/main.ts
14
src/main.ts
@ -6,14 +6,22 @@ process.env['NODE_CONFIG_DIR'] =
|
||||
+ delimiter
|
||||
+ (process.env['NODE_CONFIG_DIR'] || __dirname + '/../config/');
|
||||
|
||||
import {logger} from "swaf/Logger";
|
||||
import App from "./App";
|
||||
import config from "config";
|
||||
import {promises as fs} from "fs";
|
||||
import {logger} from "swaf/Logger";
|
||||
|
||||
import App from "./App.js";
|
||||
|
||||
(async () => {
|
||||
logger.debug('Config path:', process.env['NODE_CONFIG_DIR']);
|
||||
|
||||
const app = new App(config.get<string>('listen_addr'), config.get<number>('port'));
|
||||
const packageJson = JSON.parse((await fs.readFile('package.json')).toString());
|
||||
|
||||
const app = new App(
|
||||
packageJson.version,
|
||||
config.get<string>('app.listen_addr'),
|
||||
config.get<number>('app.port'),
|
||||
);
|
||||
await app.start();
|
||||
})().catch(err => {
|
||||
logger.error(err);
|
||||
|
30
src/tsconfig.json
Normal file
30
src/tsconfig.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
|
||||
"module": "CommonJS",
|
||||
|
||||
"baseUrl": "../dist",
|
||||
"rootDir": "./",
|
||||
"sourceRoot": "./",
|
||||
"outDir": "../dist",
|
||||
|
||||
"typeRoots": [
|
||||
"src/types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../node_modules/swaf/types"
|
||||
],
|
||||
"exclude": [
|
||||
"./assets/**/*",
|
||||
"./common/**/*"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./common"
|
||||
}
|
||||
]
|
||||
}
|
0
src/types/.gitkeep
Normal file
0
src/types/.gitkeep
Normal file
@ -1,19 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "public/js",
|
||||
"rootDir": "./assets",
|
||||
"target": "ES6",
|
||||
"strict": true,
|
||||
"lib": [
|
||||
"es2020",
|
||||
"DOM"
|
||||
],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"assets/ts/**/*"
|
||||
]
|
||||
}
|
@ -1,23 +1,43 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"esModuleInterop": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "./src",
|
||||
"target": "ES6",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"declaration": true,
|
||||
"stripInternal": true,
|
||||
|
||||
"strict": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictNullChecks": true,
|
||||
|
||||
"moduleResolution": "Node",
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": "dist",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"outDir": "dist",
|
||||
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
"src/types"
|
||||
],
|
||||
"lib": [
|
||||
"es2020",
|
||||
"DOM"
|
||||
],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
"dom"
|
||||
],
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"node_modules/swaf/types"
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "src",
|
||||
},
|
||||
{
|
||||
"path": "src/assets/ts",
|
||||
},
|
||||
{
|
||||
"path": "src/assets/views",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -11,4 +11,4 @@
|
||||
"src/types/**/*",
|
||||
"test/**/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,100 +0,0 @@
|
||||
const path = require('path');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
const dev = process.env.NODE_ENV === 'development';
|
||||
|
||||
const userConfig = require('./assets/config.json');
|
||||
for (const b in userConfig.bundles) {
|
||||
if (userConfig.bundles.hasOwnProperty(b)) {
|
||||
userConfig.bundles[b] = `./assets/${userConfig.bundles[b]}`;
|
||||
}
|
||||
}
|
||||
|
||||
const config = {
|
||||
entry: userConfig.bundles,
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'public/js'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
devtool: dev ? 'eval-source-map' : undefined,
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env'],
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: [
|
||||
{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: {
|
||||
publicPath: '/',
|
||||
}
|
||||
},
|
||||
'css-loader',
|
||||
'sass-loader',
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)$/i,
|
||||
use: 'file-loader?name=../fonts/[name].[ext]',
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/i,
|
||||
use: {
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: 'tsconfig.frontend.json',
|
||||
}
|
||||
},
|
||||
exclude: '/node_modules/'
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)$/i,
|
||||
use: [
|
||||
'file-loader?name=../img/[name].[ext]',
|
||||
{
|
||||
loader: 'img-loader',
|
||||
options: {
|
||||
enabled: !dev,
|
||||
plugins: [
|
||||
require('imagemin-gifsicle')({}),
|
||||
require('imagemin-mozjpeg')({}),
|
||||
require('imagemin-pngquant')({}),
|
||||
require('imagemin-svgo')({}),
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '../css/[name].css',
|
||||
}),
|
||||
]
|
||||
};
|
||||
|
||||
if (!dev) {
|
||||
config.optimization = {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin(),
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = config;
|
Loading…
Reference in New Issue
Block a user