mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-08 15:00:37 -04:00

- Adds a new test suite with Docker-based smoke tests for all locales. Can be run using the ./smoketest.sh script. - Replaces all calls to Math.random() with a new helper that returns 0.5 in snapshot testing mode, ensuring deterministic snapshots. - Similarly replaces all calls to new Date() and Date.now() with new helpers that return a fixed date in snapshot testing mode. - Replaces checks against NODE_ENV with APP_ENV, to ensure that the bundles can be built with Nuxt for testing without losing code that would otherwise be stripped out by production optimizations. - Adds a database init script that can be used to initialize the database with a single admin user and a long-lived JWT token for use in automation tests. - Adds a JWT decoding/encoding CLI tool for debugging JWTs. Note: Snapshots are not checked in, and must be generated manually. See test/__snapshots__/.gitignore for more information.
299 lines
9.1 KiB
TypeScript
299 lines
9.1 KiB
TypeScript
import fs from 'node:fs';
|
|
import path from 'path';
|
|
|
|
import { resolvePath } from '@nuxt/kit';
|
|
import yamlPlugin from '@rollup/plugin-yaml';
|
|
import { sentryVitePlugin } from '@sentry/vite-plugin';
|
|
import { defineNuxtConfig } from 'nuxt/config';
|
|
import type { RouteMeta } from 'vue-router';
|
|
|
|
import extendPages from './hooks/pages-extend.ts';
|
|
import type { Config } from './locale/config.ts';
|
|
import localeDescriptions from './locale/locales.ts';
|
|
import type { Translations } from './locale/translations.ts';
|
|
import mdPlugin from './plugins/rollup/md.ts';
|
|
import sumlPlugin from './plugins/rollup/suml.ts';
|
|
import tsvPlugin from './plugins/rollup/tsv.ts';
|
|
import dotenv from './server/dotenv.ts';
|
|
import { loadSuml } from './server/loader.ts';
|
|
|
|
interface TranslatedRoute {
|
|
paths: string[];
|
|
meta?: RouteMeta;
|
|
}
|
|
|
|
declare module 'nuxt/app' {
|
|
interface PageMeta {
|
|
translatedPaths?: (config: Config) => string[] | Record<string, TranslatedRoute>;
|
|
}
|
|
}
|
|
|
|
dotenv();
|
|
|
|
const config = loadSuml('config') as Config;
|
|
const translations = loadSuml('translations') as Translations;
|
|
|
|
const locale = config.locale;
|
|
const title = translations.title;
|
|
const description = translations.description;
|
|
const colour = '#C71585';
|
|
|
|
let __dirname = new URL('.', import.meta.url).pathname;
|
|
if (process.platform === 'win32') {
|
|
// A small hack, for Windows can't have nice things.
|
|
__dirname = __dirname.slice(1);
|
|
}
|
|
|
|
const versionFile = `${__dirname}/cache/version`;
|
|
const version = fs.existsSync(versionFile) ? fs.readFileSync(versionFile).toString('utf-8') : undefined;
|
|
|
|
const publicKeyFile = `${__dirname}/keys/public.pem`;
|
|
const publicKey = fs.existsSync(publicKeyFile) ? fs.readFileSync(publicKeyFile).toString('utf-8') : undefined;
|
|
|
|
const esBuildOptions = {
|
|
supported: {
|
|
'top-level-await': true,
|
|
},
|
|
};
|
|
|
|
const postCssPlugins: Record<string, object> = {
|
|
autoprefixer: {},
|
|
cssnano: {},
|
|
};
|
|
|
|
if (config.dir === 'rtl') {
|
|
postCssPlugins.rtlcss = {};
|
|
}
|
|
|
|
const hostname = process.env.HOST ?? '0.0.0.0';
|
|
const port = parseInt(process.env.PORT ?? '3000');
|
|
export default defineNuxtConfig({
|
|
modules: [
|
|
'@pinia/nuxt',
|
|
'@nuxtjs/plausible',
|
|
'@vite-pwa/nuxt',
|
|
'@nuxt/test-utils/module',
|
|
'@nuxt/eslint',
|
|
],
|
|
devtools: {
|
|
enabled: true,
|
|
},
|
|
css: [
|
|
'~/assets/style.scss',
|
|
],
|
|
vue: {
|
|
compilerOptions: {
|
|
whitespace: 'preserve',
|
|
},
|
|
},
|
|
runtimeConfig: {
|
|
public: {
|
|
env: process.env.ENV,
|
|
baseUrl: process.env.BASE_URL,
|
|
homeUrl: process.env.HOME_URL || 'https://pronouns.page',
|
|
allLocalesUrls: process.env.ALL_LOCALES_URLS?.split(','),
|
|
version,
|
|
publicKey,
|
|
turnstileSiteKey: process.env.TURNSTILE_SITEKEY,
|
|
cloudfront: process.env.CLOUDFRONT,
|
|
heartbeatLink: process.env.HEARTBEAT_LINK,
|
|
shopifyStorefrontToken: process.env.SHOPIFY_STOREFRONT_TOKEN ?? '',
|
|
plausible: {
|
|
enabled: !!process.env.PLAUSIBLE_API_HOST,
|
|
domain: process.env.PLAUSIBLE_DOMAIN || translations.domain,
|
|
apiHost: process.env.PLAUSIBLE_API_HOST,
|
|
// Disables automatic tracking of page views, meaning we have to do it manually
|
|
// If it's not done manually, a privacy issue occurs, which we *do not want*
|
|
autoPageviews: false,
|
|
},
|
|
sentry: {
|
|
dsn: process.env.SENTRY_DSN,
|
|
},
|
|
},
|
|
},
|
|
buildDir: process.env.NUXT_BUILD_DIR,
|
|
// effectively disabling the feature, see: https://nuxt.com/docs/api/nuxt-config#ignoreprefix
|
|
ignorePrefix: 'IGNORE',
|
|
sourcemap: true,
|
|
devServer: {
|
|
host: hostname, // listen on any host name
|
|
port,
|
|
},
|
|
features: {
|
|
inlineStyles: false,
|
|
},
|
|
compatibilityDate: '2024-07-06',
|
|
nitro: {
|
|
rollupConfig: {
|
|
external: [
|
|
'canvas',
|
|
'sharp',
|
|
],
|
|
},
|
|
esbuild: {
|
|
options: esBuildOptions,
|
|
},
|
|
output: {
|
|
dir: process.env.NITRO_OUTPUT_DIR,
|
|
},
|
|
},
|
|
serverHandlers: [
|
|
{
|
|
route: '/api/**',
|
|
handler: '~/server/index.ts',
|
|
},
|
|
],
|
|
vite: {
|
|
plugins: [
|
|
mdPlugin(),
|
|
sumlPlugin(),
|
|
tsvPlugin(),
|
|
yamlPlugin(),
|
|
sentryVitePlugin({
|
|
disable: !process.env.SENTRY_AUTH_TOKEN,
|
|
telemetry: false,
|
|
org: process.env.SENTRY_ORG,
|
|
project: process.env.SENTRY_PROJECT,
|
|
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
}),
|
|
],
|
|
css: {
|
|
preprocessorOptions: {
|
|
scss: {
|
|
api: 'legacy',
|
|
},
|
|
},
|
|
},
|
|
define: {
|
|
'import.meta.env.APP_ENV': JSON.stringify(process.env.APP_ENV || process.env.NODE_ENV),
|
|
'import.meta.env.RUN_SNAPSHOT_TESTS': JSON.stringify(process.env.RUN_SNAPSHOT_TESTS),
|
|
},
|
|
resolve: {
|
|
alias: {
|
|
'~@fortawesome': path.resolve(__dirname, 'node_modules/@fortawesome'),
|
|
'~@vuepic': path.resolve(__dirname, 'node_modules/@vuepic'),
|
|
'~avris-sorter': path.resolve(__dirname, 'node_modules/avris-sorter'),
|
|
'~bootstrap': path.resolve(__dirname, 'node_modules/bootstrap'),
|
|
},
|
|
},
|
|
esbuild: esBuildOptions,
|
|
optimizeDeps: {
|
|
include: [
|
|
// list dependencies which trigger “optimized dependencies changed. reloading”
|
|
// https://github.com/nuxt/nuxt/discussions/27700
|
|
'@floating-ui/vue',
|
|
'@vueuse/core',
|
|
'avris-columnist',
|
|
'avris-futurus',
|
|
'avris-sorter',
|
|
'canvas-confetti',
|
|
'chart.js',
|
|
'clipboard',
|
|
'generic-diff',
|
|
'html2canvas',
|
|
'markdown-it',
|
|
'markdown-it-mark',
|
|
'markdown-it-sub',
|
|
'markdown-it-sup',
|
|
'nepali-calendar-js',
|
|
'qr-code-styling',
|
|
'suml',
|
|
'uuid',
|
|
'vue3-lazy-hydration',
|
|
'vuedraggable',
|
|
'zh_cn_zh_tw',
|
|
],
|
|
},
|
|
server: {
|
|
watch: {
|
|
ignored: ['**/test/output/**'],
|
|
},
|
|
},
|
|
},
|
|
typescript: {
|
|
typeCheck: !process.env.RUN_SNAPSHOT_TESTS &&
|
|
(process.env.APP_ENV || process.env.NODE_ENV) !== 'production',
|
|
tsConfig: {
|
|
compilerOptions: {
|
|
baseUrl: './',
|
|
allowImportingTsExtensions: true,
|
|
},
|
|
},
|
|
},
|
|
postcss: {
|
|
plugins: postCssPlugins,
|
|
},
|
|
hooks: {
|
|
'pages:extend': extendPages(config),
|
|
async 'nitro:config'(nitroConfig) {
|
|
if (nitroConfig.publicAssets === undefined) {
|
|
nitroConfig.publicAssets = [];
|
|
}
|
|
const codes = ['_', ...localeDescriptions.map((localeDescription) => localeDescription.code)];
|
|
nitroConfig.publicAssets.push(...(await Promise.all(codes.map(async (code) => [{
|
|
baseURL: `/docs/${code}`,
|
|
dir: await resolvePath(`~/locale/${code}/docs`),
|
|
maxAge: 0,
|
|
}, {
|
|
baseURL: `/img/${code}`,
|
|
dir: await resolvePath(`~/locale/${code}/img`),
|
|
maxAge: 0,
|
|
}]))).flat());
|
|
},
|
|
},
|
|
eslint: {
|
|
config: {
|
|
stylistic: {
|
|
indent: 4,
|
|
semi: true,
|
|
braceStyle: '1tbs',
|
|
arrowParens: true,
|
|
},
|
|
},
|
|
},
|
|
pwa: {
|
|
registerType: 'autoUpdate',
|
|
manifest: {
|
|
name: title,
|
|
short_name: title,
|
|
description,
|
|
lang: locale,
|
|
background_color: '#ffffff',
|
|
theme_color: colour,
|
|
// icons generated via `pwa-assets-generator --preset minimal-2023 public/logo/logo-primary.svg`
|
|
// see https://vite-pwa-org.netlify.app/assets-generator/cli
|
|
icons: [
|
|
{
|
|
src: 'pwa-64x64.png',
|
|
sizes: '64x64',
|
|
type: 'image/png',
|
|
},
|
|
{
|
|
src: 'pwa-192x192.png',
|
|
sizes: '192x192',
|
|
type: 'image/png',
|
|
},
|
|
{
|
|
src: 'pwa-512x512.png',
|
|
sizes: '512x512',
|
|
type: 'image/png',
|
|
},
|
|
{
|
|
src: 'maskable-icon-512x512.png',
|
|
sizes: '512x512',
|
|
type: 'image/png',
|
|
purpose: 'maskable',
|
|
},
|
|
],
|
|
},
|
|
workbox: {
|
|
runtimeCaching: [
|
|
{
|
|
urlPattern: /^\/@/,
|
|
handler: 'NetworkFirst',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|