Upgrade swaf and dependencies

This commit is contained in:
Alice Gaudon 2021-11-20 19:54:59 +01:00
parent 938e8b4ebb
commit 569a50e43a
45 changed files with 276 additions and 297 deletions

3
.gitignore vendored
View File

@ -5,3 +5,6 @@ dist
yarn-error.log yarn-error.log
src/package.json src/package.json
intermediates/
dist/

View File

@ -1,10 +0,0 @@
{
"bundles": {
"app": "ts/app.ts",
"layout": "sass/layout.scss",
"error": "sass/error.scss",
"logo": "img/logo.svg",
"logo_png": "img/logox128.png",
"logo_png_xxl": "img/logox1024.png"
}
}

View File

@ -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';

View File

@ -10,47 +10,53 @@
"test": "jest --verbose --runInBand", "test": "jest --verbose --runInBand",
"clean": "node scripts/clean.js", "clean": "node scripts/clean.js",
"prepare-sources": "node scripts/prepare-sources.js", "prepare-sources": "node scripts/prepare-sources.js",
"compile": "yarn clean && tsc", "compile": "yarn clean && yarn prepare-sources && tsc --build",
"build": "yarn prepare-sources && yarn compile && webpack --mode production", "build": "yarn compile && node . pre-compile-views && 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\" \"webpack --watch --mode development\" \"maildev\"", "build-production": "NODE_ENV=production yarn build",
"start": "yarn build && node", "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 ." "lint": "eslint .",
"start": "yarn build-production && node ."
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.9.0", "@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5", "@babel/preset-env": "^7.9.5",
"@fortawesome/fontawesome-free": "^5.14.0", "@fortawesome/fontawesome-free": "^5.14.0",
"@types/config": "^0.0.38", "@tsconfig/svelte": "^2.0.1",
"@types/config": "^0.0.40",
"@types/express": "^4.17.6", "@types/express": "^4.17.6",
"@types/express-session": "^1.17.0", "@types/express-session": "^1.17.0",
"@types/feather-icons": "^4.7.0", "@types/feather-icons": "^4.7.0",
"@types/formidable": "^1.0.31", "@types/formidable": "^2.0.0",
"@types/jest": "^26.0.4", "@types/jest": "^27.0.3",
"@types/mysql": "^2.15.15", "@types/mysql": "^2.15.15",
"@types/node": "^15.0.1", "@types/node": "^16.11.9",
"@types/nodemailer": "^6.4.0", "@types/nodemailer": "^6.4.0",
"@types/nunjucks": "^3.1.3", "@types/nunjucks": "^3.1.3",
"@types/ws": "^7.2.6", "@types/ws": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^4.3.0", "@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^4.3.0", "@typescript-eslint/parser": "^5.4.0",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"clear-module": "^4.1.2",
"concurrently": "^6.0.0", "concurrently": "^6.0.0",
"css-loader": "^5.0.0", "css-loader": "^6.5.1",
"eslint": "^7.10.0", "eslint": "^8.2.0",
"feather-icons": "^4.28.0", "feather-icons": "^4.28.0",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"image-minimizer-webpack-plugin": "^2.2.0", "image-minimizer-webpack-plugin": "^2.2.0",
"imagemin-gifsicle": "^7.0.0", "imagemin-gifsicle": "^7.0.0",
"imagemin-mozjpeg": "^9.0.0", "imagemin-mozjpeg": "^9.0.0",
"imagemin-pngquant": "^9.0.2", "imagemin-pngquant": "^9.0.2",
"imagemin-svgo": "^9.0.0", "imagemin-svgo": "^10.0.0",
"imagemin-webp": "^6.0.0", "imagemin-webp": "^6.0.0",
"jest": "^27.0.4", "jest": "^27.0.4",
"maildev": "^1.1.0", "maildev": "^1.1.0",
"mini-css-extract-plugin": "^1.2.1", "mini-css-extract-plugin": "^2.4.5",
"nodemon": "^2.0.3", "nodemon": "^2.0.3",
"normalize.css": "^8.0.1",
"sass": "^1.32.12", "sass": "^1.32.12",
"sass-loader": "^12.0.0", "sass-loader": "^12.0.0",
"svelte": "^3.44.2",
"svelte-preprocess": "^4.9.8",
"svgo": "^2.3.0", "svgo": "^2.3.0",
"terser-webpack-plugin": "^5.0.3", "terser-webpack-plugin": "^5.0.3",
"ts-jest": "^27.0.3", "ts-jest": "^27.0.3",
@ -62,6 +68,6 @@
"dependencies": { "dependencies": {
"config": "^3.3.1", "config": "^3.3.1",
"express": "^4.17.1", "express": "^4.17.1",
"swaf": "^0.23.0" "swaf": "^0.24.4"
} }
} }

22
scripts/_functions.js Normal file
View 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,
};

View File

@ -1,7 +1,9 @@
const fs = require('fs'); const fs = require('fs');
[ [
'intermediates',
'dist', 'dist',
'public',
].forEach(file => { ].forEach(file => {
if (fs.existsSync(file)) { if (fs.existsSync(file)) {
console.log('Cleaning', file, '...'); console.log('Cleaning', file, '...');

22
scripts/dist.js Normal file
View 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');
});

View File

@ -1,4 +1,28 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); 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');

View File

@ -2,7 +2,6 @@ import Application from "swaf/Application";
import Migration, {MigrationType} from "swaf/db/Migration"; import Migration, {MigrationType} from "swaf/db/Migration";
import CreateMigrationsTable from "swaf/migrations/CreateMigrationsTable"; import CreateMigrationsTable from "swaf/migrations/CreateMigrationsTable";
import ExpressAppComponent from "swaf/components/ExpressAppComponent"; import ExpressAppComponent from "swaf/components/ExpressAppComponent";
import NunjucksComponent from "swaf/components/NunjucksComponent";
import MysqlComponent from "swaf/components/MysqlComponent"; import MysqlComponent from "swaf/components/MysqlComponent";
import LogRequestsComponent from "swaf/components/LogRequestsComponent"; import LogRequestsComponent from "swaf/components/LogRequestsComponent";
import RedisComponent from "swaf/components/RedisComponent"; import RedisComponent from "swaf/components/RedisComponent";
@ -18,14 +17,22 @@ import AutoUpdateComponent from "swaf/components/AutoUpdateComponent";
import DummyMigration from "swaf/migrations/DummyMigration"; import DummyMigration from "swaf/migrations/DummyMigration";
import DropLegacyLogsTable from "swaf/migrations/DropLegacyLogsTable"; import DropLegacyLogsTable from "swaf/migrations/DropLegacyLogsTable";
import PreviousUrlComponent from "swaf/components/PreviousUrlComponent"; import PreviousUrlComponent from "swaf/components/PreviousUrlComponent";
import packageJson = require('./package.json'); import MailViewEngine from "swaf/frontend/MailViewEngine";
import AssetCompiler from "swaf/frontend/AssetCompiler";
import FrontendToolsComponent from "swaf/components/FrontendToolsComponent";
import CopyAssetPreCompiler from "swaf/frontend/CopyAssetPreCompiler";
import ScssAssetPreCompiler from "swaf/frontend/ScssAssetPreCompiler";
import TypeScriptPreCompiler from "swaf/frontend/TypeScriptPreCompiler";
import SvelteViewEngine from "swaf/frontend/SvelteViewEngine";
import NunjucksViewEngine from "swaf/frontend/NunjucksViewEngine";
export default class App extends Application { export default class App extends Application {
public constructor( public constructor(
version: string,
private readonly addr: string, private readonly addr: string,
private readonly port: number, private readonly port: number,
) { ) {
super(packageJson.version); super(version);
} }
protected getMigrations(): MigrationType<Migration>[] { protected getMigrations(): MigrationType<Migration>[] {
@ -52,18 +59,27 @@ export default class App extends Application {
this.use(new ServeStaticDirectoryComponent('node_modules/feather-icons/dist', '/icons')); this.use(new ServeStaticDirectoryComponent('node_modules/feather-icons/dist', '/icons'));
// Dynamic views and routes // 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()); this.use(new PreviousUrlComponent());
// Maintenance // Maintenance
this.use(new MaintenanceComponent(this, () => { this.use(new MaintenanceComponent());
return this.as(RedisComponent).canServe() && this.as(MysqlComponent).canServe();
}));
this.use(new AutoUpdateComponent()); this.use(new AutoUpdateComponent());
// Services // Services
this.use(new MysqlComponent()); this.use(new MysqlComponent());
this.use(new MailComponent()); this.use(new MailComponent(new MailViewEngine(intermediateDirectory, ...additionalViewPaths)));
// Session // Session
this.use(new RedisComponent()); this.use(new RedisComponent());
@ -76,7 +92,7 @@ export default class App extends Application {
this.use(new CsrfProtectionComponent()); this.use(new CsrfProtectionComponent());
// WebSocket server // WebSocket server
this.use(new WebSocketServerComponent(this, this.as(ExpressAppComponent), this.as(RedisComponent))); this.use(new WebSocketServerComponent());
} }
private registerWebSocketListeners() { private registerWebSocketListeners() {

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

3
src/assets/package.json Normal file
View File

@ -0,0 +1,3 @@
{
"type": "commonjs"
}

View File

@ -682,33 +682,6 @@ td.actions {
} }
} }
.data-table {
width: 100%;
text-align: left;
border-collapse: collapse;
th, td {
padding: 8px;
}
th {
border-bottom: 1px solid #39434a;
white-space: nowrap;
}
tr:nth-child(even) {
background-color: rgba(255, 255, 255, 0.03);
}
tr:hover {
background-color: rgba(255, 255, 255, 0.09);
}
thead tr:hover {
background-color: transparent;
}
}
// --- // ---
// --- Breadcrumb widget // --- Breadcrumb widget
@ -766,82 +739,9 @@ td.actions {
} }
// ---
// --- Feather
// ---
.feather {
display: inline-flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
width: var(--icon-size);
height: var(--icon-size);
--icon-size: 16px;
font-size: var(--icon-size);
stroke: currentColor;
stroke-width: 2;
stroke-linecap: square;
stroke-linejoin: miter;
fill: none;
vertical-align: middle;
h1 > &, h2 > &, h3 > & {
--icon-size: 24px;
}
}
// --- // ---
// --- Helper classes // --- Helper classes
// --- // ---
.message {
display: flex;
flex-direction: row;
align-items: center;
padding: 8px 16px;
border-radius: 5px;
.feather {
--icon-size: 24px;
margin-right: 8px;
}
&:not(&-discreet) {
background-color: rgba(255, 255, 255, 0.33);
&[data-type=info], &[data-type=question] {
background-color: $infoColor;
}
&[data-type=success] {
background-color: $successColor;
}
&[data-type=warning] {
background-color: $warningColor;
}
&[data-type=error] {
background-color: $errorColor;
}
}
&-discreet {
color: mix($panelBackground, #fff, 35%);
.feather {
--icon-size: 20px;
}
}
}
.messages .message:not(:last-child) {
margin-bottom: 8px;
}
.container > .messages:first-child { .container > .messages:first-child {
margin-top: 16px; margin-top: 16px;
} }

View File

@ -0,0 +1,4 @@
import '@fortawesome/fontawesome-free/scss/fontawesome.scss';
import '@fortawesome/fontawesome-free/scss/regular.scss';
import '@fortawesome/fontawesome-free/scss/solid.scss';
import '@fortawesome/fontawesome-free/scss/brands.scss';

View File

@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"include": [
"./**/*"
]
}

View 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"
}
]
}

View 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
View File

@ -0,0 +1 @@
console.log('common code between back and front');

3
src/common/package.json Normal file
View File

@ -0,0 +1,3 @@
{
"type": "commonjs"
}

20
src/common/tsconfig.json Normal file
View 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": [
"./**/*"
]
}

View File

@ -1,5 +1,6 @@
import Controller from "swaf/Controller"; import Controller from "swaf/Controller";
import {Request, Response} from "express"; import {Request, Response} from "express";
import {route} from "swaf/common/Routing";
export default class HomeController extends Controller { export default class HomeController extends Controller {
public routes(): void { 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 * This is to test and assert that swaf extended types are available
*/ */
protected async goBack(req: Request, res: Response): Promise<void> { protected async goBack(req: Request, res: Response): Promise<void> {
res.redirect(req.getPreviousUrl() || Controller.route('home')); res.redirect(req.getPreviousUrl() || route('home'));
} }
} }

View File

@ -9,11 +9,18 @@ process.env['NODE_CONFIG_DIR'] =
import {logger} from "swaf/Logger"; import {logger} from "swaf/Logger";
import App from "./App"; import App from "./App";
import config from "config"; import config from "config";
import {promises as fs} from "fs";
(async () => { (async () => {
logger.debug('Config path:', process.env['NODE_CONFIG_DIR']); 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(); await app.start();
})().catch(err => { })().catch(err => {
logger.error(err); logger.error(err);

30
src/tsconfig.json Normal file
View 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"
}
]
}

View 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/**/*"
]
}

View File

@ -1,22 +1,43 @@
{ {
"compilerOptions": { "compilerOptions": {
"module": "CommonJS", "target": "ESNext",
"esModuleInterop": true, "module": "ESNext",
"outDir": "dist", "declaration": true,
"rootDir": "./src", "stripInternal": true,
"target": "ES6",
"strict": 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": [ "lib": [
"es2020", "es2020",
"DOM" "dom"
], ],
"typeRoots": [ "resolveJsonModule": true,
"./node_modules/@types" "skipLibCheck": true,
], "allowJs": true
"resolveJsonModule": true
}, },
"include": [ "include": [],
"src/**/*", "references": [
"node_modules/swaf/types" {
"path": "src",
},
{
"path": "src/assets/ts",
},
{
"path": "src/assets/views",
}
] ]
} }

View File

@ -1,121 +0,0 @@
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const {extendDefaultPlugins} = require("svgo");
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]',
],
type: 'asset',
}
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new MiniCssExtractPlugin({
filename: '../css/[name].css',
}),
new ImageMinimizerPlugin({
minimizerOptions: {
// Lossless optimization with custom option
// Feel free to experiment with options for better result for you
plugins: [
["gifsicle", {}],
["mozjpeg", {}],
["pngquant", {}],
["webp", {quality: 90}],
// Svgo configuration here https://github.com/svg/svgo#configuration
[
"svgo",
{
plugins: extendDefaultPlugins([
{
name: "removeViewBox",
active: false,
},
{
name: "addAttributesToSVGElement",
params: {
attributes: [{xmlns: "http://www.w3.org/2000/svg"}],
},
},
]),
},
],
],
},
}),
]
};
if (!dev) {
config.optimization = {
minimize: true,
minimizer: [
new TerserPlugin(),
]
};
}
module.exports = config;