Use Vite exclusively as frontend build system; remove Gulp.

This commit is contained in:
Buster Neece 2023-08-04 13:34:40 -05:00
parent 49a11fb2a7
commit 53340ba20e
No known key found for this signature in database
25 changed files with 89 additions and 7524 deletions

View File

@ -89,9 +89,7 @@ jobs:
- name: Set console permissions and clear static assets.
run: |
rm -rf web/static/dist
rm -rf web/static/vite_dist
rm -rf web/static/assets.json
rm -rf translations/*.UTF-8
chmod a+x bin/console
@ -145,9 +143,7 @@ jobs:
path: |
.gitinfo
translations
web/static/dist
web/static/vite_dist
web/static/assets.json
web/static/api/openapi.yml
build:

4
.gitignore vendored
View File

@ -32,12 +32,8 @@ tmp/cache/*---*
!/vendor/.gitkeep
# NPM built content
/web/static/dist/*
/web/static/dist/**/*
!/web/static/dist/.gitkeep
/web/static/vite_dist/**/*
!/web/static/vite_dist/.gitkeep
/web/static/assets.json
/translations/frontend.pot
# Ansible deployment files

View File

@ -1,120 +0,0 @@
'use strict';
import gulp from 'gulp';
import babel from 'gulp-babel';
import {deleteAsync as del} from 'del';
import rev from 'gulp-rev';
import uglify from 'gulp-uglify';
import gulp_sourcemaps from 'gulp-sourcemaps';
import sass from 'gulp-dart-sass';
import clean_css from 'gulp-clean-css';
import revdel from 'gulp-rev-delete-original';
import gulpmode from 'gulp-mode';
import run_command from 'gulp-run-command';
const { task, src, dest, parallel, watch, series } = gulp;
const { manifest } = rev;
const { init, write } = gulp_sourcemaps;
const mode = gulpmode();
const run = run_command.default;
const jsFiles = {
'bootstrap': {
base: null,
files: [
'node_modules/bootstrap/dist/js/bootstrap.bundle.min.js'
]
},
'material-icons': {
files: [
'font/*'
]
},
'roboto-fontface': {
base: 'node_modules/roboto-fontface',
files: [
'node_modules/roboto-fontface/css/roboto/roboto-fontface.css',
'node_modules/roboto-fontface/fonts/roboto/*'
]
},
'webcaster': {
base: null,
files: [
'js/webcaster/*.js'
]
},
'translations': {
base: '../translations',
files: [
'../translations/*/translations.json',
]
}
};
const defaultTasks = Object.keys(jsFiles);
defaultTasks.forEach(function (libName) {
task('scripts:' + libName, function () {
return src(jsFiles[libName].files, {
base: jsFiles[libName].base
}).pipe(dest('../web/static/dist/lib/' + libName));
});
});
task('bundle-deps', parallel(
defaultTasks.map(function (name) {
return 'scripts:' + name;
})
));
task('clean', function () {
return del([
'../web/static/dist/**/*',
'../web/static/vite_dist/assets/**/*',
'../web/static/vite_dist/manifest.json',
'../web/static/assets.json',
], {force: true});
});
task('concat-js', function () {
return src('./js/app.js')
.pipe(init())
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(uglify())
.pipe(write())
.pipe(dest('../web/static/dist'));
});
task('build-vue', run('vite build'));
task('build-css', function () {
return src(['./scss/style.scss'])
.pipe(mode.development(init()))
.pipe(sass({
includePaths: ['node_modules']
}))
.pipe(clean_css())
.pipe(mode.development(write()))
.pipe(dest('../web/static/dist'));
});
task('watch', function () {
watch([
'./vue/**',
'./js/**/*.js',
'./scss/**',
], buildAll);
});
const buildAll = series('clean', parallel('concat-js', 'build-vue', 'build-css', 'bundle-deps'), function () {
return src(['../web/static/dist/**/*.{js,json,css}'], {base: '../web/static/'})
.pipe(rev())
.pipe(revdel())
.pipe(dest('../web/static/'))
.pipe(manifest('assets.json'))
.pipe(dest('../web/static/'));
});
export { buildAll as default };

View File

@ -1,3 +1,7 @@
import '../scss/style.scss';
import * as bootstrap from 'bootstrap';
const ready = (callback) => {
if (document.readyState !== "loading") callback();
else document.addEventListener("DOMContentLoaded", callback);
@ -63,3 +67,5 @@ ready(() => {
toast.show();
});
});
export default bootstrap;

View File

@ -2,7 +2,12 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["vue/*"]
"!/*": [
"*"
],
"~/*": [
"vue/*"
]
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,8 @@
"name": "azuracast",
"license": "Apache-2.0",
"scripts": {
"build": "gulp",
"watch": "gulp watch",
"generate-locales": "vue-gettext-extract",
"vite-build": "vite build"
"build": "vite build",
"generate-locales": "vue-gettext-extract"
},
"dependencies": {
"@codemirror/lang-css": "^6.0.1",
@ -48,8 +46,6 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"@types/luxon": "^3.3.1",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
@ -59,16 +55,6 @@
"eslint": "^8.45.0",
"eslint-plugin-vue": "^9.8.0",
"glob": "^10.2.7",
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0",
"gulp-clean-css": "^4",
"gulp-dart-sass": "^1.0.2",
"gulp-mode": "^1.1.0",
"gulp-rev": "^10",
"gulp-rev-delete-original": "^0.2.3",
"gulp-run-command": "0.0.10",
"gulp-sourcemaps": "^3",
"gulp-uglify": "^3.0.2",
"sass": "^1.39.2",
"typescript": "^5.1.6",
"vite": "^4.4.6",

View File

@ -1,11 +1,11 @@
:root,
[data-bs-theme="light"] {
--public-page-bg: url('../img/hexbg.webp');
--public-page-bg: url('/static/img/hexbg.webp');
--scrollbar-color: #{$gray-400};
}
@include color-mode(dark, true) {
--public-page-bg: url('../img/hexbg_dark.webp');
--public-page-bg: url('/static/img/hexbg_dark.webp');
--scrollbar-color: #{$gray-800};
--bs-info-bg-subtle: #bbdefb;
--bs-info-text-emphasis: #181d20;

View File

@ -1,5 +1,5 @@
header.navbar {
background-image: url('../img/header_bg.webp'), linear-gradient(90deg, #0a6fc2 0%, #2196f3 100%);
background-image: url('/static/img/header_bg.webp'), linear-gradient(90deg, #0a6fc2 0%, #2196f3 100%);
background-position: top left, center;
background-repeat: no-repeat;
background-size: 500px 100px, cover;

View File

@ -1,19 +1,5 @@
@use "sass:math";
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url('lib/material-icons/material-icons.woff2') format('woff2');
}
@font-face {
font-family: 'Material Icons Outlined';
font-style: normal;
font-weight: 400;
src: url('lib/material-icons/material-icons-outlined.woff2') format('woff2');
}
@mixin reset-material-icons($size: $material-icon-size) {
font-size: $size;
line-height: 1;

View File

@ -1,3 +1,19 @@
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url('../font/material-icons.woff2') format('woff2');
}
@font-face {
font-family: 'Material Icons Outlined';
font-style: normal;
font-weight: 400;
src: url('../font/material-icons-outlined.woff2') format('woff2');
}
@import "roboto-fontface/css/roboto/roboto-fontface.css";
// Include functions first (so you can manipulate colors, SVGs, calc, etc)
@import "bootstrap/scss/functions";

View File

@ -1 +1 @@
@import '@vuepic/vue-datepicker/src/VueDatePicker/style/main';
@import '@vuepic/vue-datepicker/dist/main.css';

View File

@ -2,6 +2,9 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"!/*": [
"*"
],
"~/*": [
"vue/*"
]
@ -32,7 +35,6 @@
"vue/**/*.ts",
"vue/**/*.d.ts",
"vue/**/*.tsx",
"vue/**/*.vue",
"vue/**/*.js"
],
"vue/**/*.vue"
]
}

View File

@ -4,20 +4,26 @@ import {glob} from "glob";
import {resolve} from "path";
import eslint from "vite-plugin-eslint";
const inputs = glob.sync('./vue/pages/**/*.js').reduce((acc, path) => {
// vue/pages/Admin/Index becomes AdminIndex
const entry = path.replace(/\.js$/g, '')
.replace(/^vue\/pages\//g, '')
.replace(/\//g, '');
acc[entry] = resolve(__dirname, path)
return acc
}, {});
inputs['Layout'] = resolve(__dirname, './js/layout.js');
console.log(inputs);
// https://vitejs.dev/config/
export default defineConfig({
base: '/static/vite_dist',
build: {
rollupOptions: {
input: glob.sync('./vue/pages/**/*.js').reduce((acc, path) => {
// vue/pages/Admin/Index becomes AdminIndex
const entry = path.replace(/\.js$/g, '')
.replace(/^vue\/pages\//g, '')
.replace(/\//g, '');
acc[entry] = resolve(__dirname, path)
return acc
}, {}),
input: inputs,
output: {
manualChunks: {
vue: ['vue'],
@ -28,11 +34,12 @@ export default defineConfig({
}
},
manifest: true,
emptyOutDir: false,
emptyOutDir: true,
outDir: resolve(__dirname, '../web/static/vite_dist')
},
resolve: {
alias: {
'!': resolve(__dirname),
'~': resolve(__dirname, './vue')
},
extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json', '.vue']

View File

@ -4,7 +4,6 @@ interface AzuraCastConstants {
locale: string,
localeShort: string,
localeWithDashes: string,
localePaths: object,
timeConfig: object,
apiCsrf: string | null,
enableAdvancedFeatures: boolean
@ -15,7 +14,6 @@ export function useAzuraCast(): AzuraCastConstants {
locale: App.locale ?? 'en_US',
localeShort: App.locale_short ?? 'en',
localeWithDashes: App.locale_with_dashes ?? 'en-US',
localePaths: App.locale_paths ?? {},
timeConfig: App.time_config ?? {},
apiCsrf: App.api_csrf ?? null,
enableAdvancedFeatures: App.enable_advanced_features ?? true

View File

@ -1,9 +1,8 @@
import {createGettext, Language} from "vue3-gettext";
import {useAzuraCast} from "~/vendor/azuracast";
import axios from "axios";
import {App} from "vue";
const {locale, localePaths} = useAzuraCast();
const {locale} = useAzuraCast();
const gettext = createGettext({
defaultLanguage: locale,
@ -11,13 +10,12 @@ const gettext = createGettext({
silent: true
});
if (localePaths[locale] !== undefined) {
axios.get(
localePaths[locale]
).then(r => {
gettext.translations = r.data;
}).catch((e) => {
console.error(e);
const translations = import.meta.glob('../../../translations/**/translations.json', {as: 'json'});
const localePath = '../../../translations/' + locale + '.UTF-8/translations.json';
if (localePath in translations) {
translations[localePath]().then((data) => {
gettext.translations = data;
});
}

View File

@ -70,9 +70,7 @@ final class View extends Engine
$vueComponents = Json::loadFromFile($environment->getBaseDirectory() . '/web/static/vite_dist/manifest.json');
$this->registerFunction(
'getVueComponentInfo',
function (string $component) use ($vueComponents) {
$componentPath = 'vue/pages/' . $component . '.js';
function (string $componentPath) use ($vueComponents) {
if (!isset($vueComponents[$componentPath])) {
return null;
}
@ -125,26 +123,6 @@ final class View extends Engine
}
);
$versionedFiles = Json::loadFromFile($environment->getBaseDirectory() . '/web/static/assets.json');
$this->registerFunction(
'assetUrl',
function (string $url) use ($environment, $versionedFiles): string {
if (isset($versionedFiles[$url])) {
$url = $versionedFiles[$url];
}
if (str_starts_with($url, 'http')) {
return $url;
}
if (str_starts_with($url, '/')) {
return $url;
}
return $environment->getAssetUrl() . '/' . $url;
}
);
$dispatcher->dispatch(new Event\BuildView($this));
}

View File

@ -19,7 +19,7 @@ $this->fetch('frontend/public/partials/station-custom', ['station' => $station])
$sections->appendStart('bodyjs');
?>
<script src="<?= $this->assetUrl('dist/lib/webcaster/taglib.js') ?>"></script>
<script src="/static/js/taglib.js"></script>
<?php
$sections->end();

View File

@ -22,17 +22,6 @@ if (null !== $show24Hours) {
$timeConfig->hour12 = !$show24Hours;
}
$localePaths = [];
foreach (App\Enums\SupportedLocales::cases() as $supportedLocale) {
if ($supportedLocale === App\Enums\SupportedLocales::default()) {
continue;
}
$localePaths[$supportedLocale->getLocaleWithoutEncoding()] = $this->assetUrl(
'dist/lib/translations/' . $supportedLocale->value . '/translations.json'
);
}
// CSRF token
$csrf = null;
@ -47,7 +36,6 @@ $app = [
'locale' => $locale,
'locale_short' => $localeShort,
'locale_with_dashes' => $localeWithDashes,
'locale_paths' => $localePaths,
'time_config' => $timeConfig,
'api_csrf' => $csrf,
'enable_advanced_features' => $customization->enableAdvancedFeatures(),

View File

@ -3,14 +3,31 @@
* @var App\Customization $customization
*/
$componentDeps = $this->getVueComponentInfo('js/layout.js');
echo <<<HTML
<script type="module" src="{$componentDeps['js']}"></script>
<script>
function ready(callback) {
if (document.readyState !== "loading") callback();
else document.addEventListener("DOMContentLoaded", callback);
}
</script>
HTML;
foreach ($componentDeps['prefetch'] as $prefetchDep) {
echo <<<HTML
<link rel="modulepreload" href="{$prefetchDep}" />
HTML;
}
foreach ($componentDeps['css'] as $cssDep) {
echo <<<HTML
<link rel="stylesheet" href="{$cssDep}" />
HTML;
}
?>
<script src="<?= $this->assetUrl('dist/lib/bootstrap/bootstrap.bundle.min.js') ?>"></script>
<script src="<?= $this->assetUrl('dist/app.js') ?>"></script>
<link rel="stylesheet" href="<?= $this->assetUrl('dist/lib/roboto-fontface/css/roboto/roboto-fontface.css') ?>">
<link rel="stylesheet" href="<?= $this->assetUrl('dist/style.css') ?>">
<link rel="apple-touch-icon" sizes="57x57" href="<?= $customization->getBrowserIconUrl(57) ?>">
<link rel="apple-touch-icon" sizes="60x60" href="<?= $customization->getBrowserIconUrl(60) ?>">
<link rel="apple-touch-icon" sizes="72x72" href="<?= $customization->getBrowserIconUrl(72) ?>">

View File

@ -6,7 +6,7 @@
* @var App\View\GlobalSections $sections
*/
$componentDeps = $this->getVueComponentInfo($component);
$componentDeps = $this->getVueComponentInfo('vue/pages/' . $component . '.js');
$propsJson = json_encode($props, JSON_THROW_ON_ERROR);

View File

@ -110,9 +110,6 @@ server {
try_files $uri =404;
}
location /static/dist {
expires 365d;
}
location /static/vite_dist {
expires 365d;
}

View File

@ -1 +0,0 @@
.gitkeep

View File

@ -1 +0,0 @@
.gitkeep