Merge branch 'timvisee:master' into master

This commit is contained in:
HrBingR 2022-08-11 23:06:47 +02:00 committed by GitHub
commit df9c7ea734
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 23533 additions and 4150 deletions

View File

@ -5,21 +5,24 @@ env:
extends: extends:
- eslint:recommended - eslint:recommended
- prettier - prettier
- plugin:node/recommended - plugin:n/recommended
- plugin:security/recommended - plugin:security/recommended
plugins: plugins:
- node - n
- security - security
root: true root: true
rules: rules:
node/no-deprecated-api: off n/no-deprecated-api: off
node/no-unsupported-features/es-syntax: off n/no-unsupported-features/es-syntax: off
node/no-unsupported-features/node-builtins: off n/no-unsupported-features/node-builtins: off
node/no-unpublished-require: off n/no-unpublished-require: off
node/no-unpublished-import: off n/no-unpublished-import: off
n/no-process-exit: off
# This forces using file extensions in imports, which is a best practice, but refactoring would take some time
n/no-missing-import: off
security/detect-non-literal-fs-filename: off security/detect-non-literal-fs-filename: off
security/detect-object-injection: off security/detect-object-injection: off

View File

@ -11,3 +11,5 @@ rules:
selector-list-comma-newline-after: null selector-list-comma-newline-after: null
value-list-comma-newline-after: null value-list-comma-newline-after: null
at-rule-no-unknown: null at-rule-no-unknown: null
# Conflicts with prettier
string-quotes: null

View File

@ -63,7 +63,6 @@ COPY --chown=app:app server server
COPY --chown=app:app --from=builder /app/dist dist COPY --chown=app:app --from=builder /app/dist dist
RUN npm ci --production && npm cache clean --force RUN npm ci --production && npm cache clean --force
RUN mkdir -p /app/.config/configstore
RUN ln -s dist/version.json version.json RUN ln -s dist/version.json version.json
ENV PORT=1443 ENV PORT=1443

View File

@ -6,4 +6,4 @@ parserOptions:
sourceType: module sourceType: module
rules: rules:
node/no-unsupported-features: off n/no-unsupported-features: off

View File

@ -45,14 +45,8 @@ async function checkCrypto() {
); );
return true; return true;
} catch (err) { } catch (err) {
try {
window.asmCrypto = await import('asmcrypto.js');
await import('@dannycoates/webcrypto-liner/build/shim');
return true;
} catch (e) {
return false; return false;
} }
}
} }
function checkStreams() { function checkStreams() {
@ -66,25 +60,12 @@ function checkStreams() {
} }
} }
async function polyfillStreams() {
try {
await import('@mattiasbuelens/web-streams-polyfill');
return true;
} catch (e) {
return false;
}
}
export default async function getCapabilities() { export default async function getCapabilities() {
const browser = browserName(); const browser = browserName();
const isMobile = /mobi|android/i.test(navigator.userAgent); const isMobile = /mobi|android/i.test(navigator.userAgent);
const serviceWorker = 'serviceWorker' in navigator && browser !== 'edge'; const serviceWorker = 'serviceWorker' in navigator && browser !== 'edge';
let crypto = await checkCrypto(); let crypto = await checkCrypto();
const nativeStreams = checkStreams(); const nativeStreams = checkStreams();
let polyStreams = false;
if (!nativeStreams) {
polyStreams = await polyfillStreams();
}
let account = typeof AUTH_CONFIG !== 'undefined'; let account = typeof AUTH_CONFIG !== 'undefined';
try { try {
account = account && !!localStorage; account = account && !!localStorage;
@ -106,10 +87,10 @@ export default async function getCapabilities() {
account, account,
crypto, crypto,
serviceWorker, serviceWorker,
streamUpload: nativeStreams || polyStreams, streamUpload: nativeStreams,
streamDownload: streamDownload:
nativeStreams && serviceWorker && browser !== 'safari' && !mobileFirefox, nativeStreams && serviceWorker && browser !== 'safari' && !mobileFirefox,
multifile: nativeStreams || polyStreams, multifile: nativeStreams,
share, share,
standalone standalone
}; };

View File

@ -48,7 +48,7 @@ class ECETransformer {
name: 'AES-GCM', name: 'AES-GCM',
length: 128 length: 128
}, },
true, // Edge polyfill requires key to be extractable to encrypt :/ false,
['encrypt', 'decrypt'] ['encrypt', 'decrypt']
); );
} }

View File

@ -1,8 +1,8 @@
import { FluentBundle } from '@fluent/bundle'; import { FluentBundle, FluentResource } from '@fluent/bundle';
function makeBundle(locale, ftl) { function makeBundle(locale, ftl) {
const bundle = new FluentBundle(locale, { useIsolating: false }); const bundle = new FluentBundle(locale, { useIsolating: false });
bundle.addMessages(ftl); bundle.addResource(new FluentResource(ftl));
return bundle; return bundle;
} }
@ -19,7 +19,7 @@ export async function getTranslator(locale) {
return function(id, data) { return function(id, data) {
for (let bundle of bundles) { for (let bundle of bundles) {
if (bundle.hasMessage(id)) { if (bundle.hasMessage(id)) {
return bundle.format(bundle.getMessage(id), data); return bundle.formatPattern(bundle.getMessage(id).value, data);
} }
} }
}; };

View File

@ -7,17 +7,14 @@ html {
@tailwind components; @tailwind components;
:not(input) { :not(input) {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
} }
:root { :root {
--violet-gradient: linear-gradient( --violet-gradient: linear-gradient(
-180deg, -180deg,
rgba(144, 89, 255, 0.8) 0%, rgb(144 89 255 / 80%) 0%,
rgba(144, 89, 255, 0.4) 100% rgb(144 89 255 / 40%) 100%
); );
} }
@ -71,7 +68,7 @@ body {
.checkbox > label::before { .checkbox > label::before {
/* @apply bg-grey-10; */ /* @apply bg-grey-10; */
@apply border; @apply border-default;
@apply rounded-sm; @apply rounded-sm;
content: ''; content: '';
@ -204,19 +201,18 @@ progress::-webkit-progress-value {
background-image: -webkit-linear-gradient( background-image: -webkit-linear-gradient(
-45deg, -45deg,
transparent 20%, transparent 20%,
rgba(255, 255, 255, 0.4) 20%, rgb(255 255 255 / 40%) 20%,
rgba(255, 255, 255, 0.4) 40%, rgb(255 255 255 / 40%) 40%,
transparent 40%, transparent 40%,
transparent 60%, transparent 60%,
rgba(255, 255, 255, 0.4) 60%, rgb(255 255 255 / 40%) 60%,
rgba(255, 255, 255, 0.4) 80%, rgb(255 255 255 / 40%) 80%,
transparent 80% transparent 80%
), ),
-webkit-linear-gradient(left, var(--color-primary), var(--color-primary)); -webkit-linear-gradient(left, var(--color-primary), var(--color-primary));
/* stylelint-enable */ /* stylelint-enable */
border-radius: 2px; border-radius: 2px;
background-size: 21px 20px, 100% 100%, 100% 100%; background-size: 21px 20px, 100% 100%, 100% 100%;
-webkit-animation: animate-stripes 1s linear infinite;
} }
progress::-moz-progress-bar { progress::-moz-progress-bar {
@ -224,12 +220,12 @@ progress::-moz-progress-bar {
background-image: -moz-linear-gradient( background-image: -moz-linear-gradient(
135deg, 135deg,
transparent 20%, transparent 20%,
rgba(255, 255, 255, 0.4) 20%, rgb(255 255 255 / 40%) 20%,
rgba(255, 255, 255, 0.4) 40%, rgb(255 255 255 / 40%) 40%,
transparent 40%, transparent 40%,
transparent 60%, transparent 60%,
rgba(255, 255, 255, 0.4) 60%, rgb(255 255 255 / 40%) 60%,
rgba(255, 255, 255, 0.4) 80%, rgb(255 255 255 / 40%) 80%,
transparent 80% transparent 80%
), ),
-moz-linear-gradient(left, var(--color-primary), var(--color-primary)); -moz-linear-gradient(left, var(--color-primary), var(--color-primary));
@ -239,12 +235,6 @@ progress::-moz-progress-bar {
animation: animate-stripes 1s linear infinite; animation: animate-stripes 1s linear infinite;
} }
@-webkit-keyframes animate-stripes {
100% {
background-position: -21px 0;
}
}
@keyframes animate-stripes { @keyframes animate-stripes {
100% { 100% {
background-position: -21px 0; background-position: -21px 0;
@ -313,7 +303,7 @@ select {
@screen md { @screen md {
.main > section { .main > section {
@apply border; @apply border-default;
@apply border-grey-80; @apply border-grey-80;
} }
} }
@ -323,13 +313,12 @@ select {
@responsive { @responsive {
.shadow-light { .shadow-light {
box-shadow: 0 0 8px 0 rgba(12, 12, 13, 0.1); box-shadow: 0 0 8px 0 rgb(12 12 13 / 10%);
} }
.shadow-big { .shadow-big {
box-shadow: 0 12px 18px 2px rgba(34, 0, 51, 0.04), box-shadow: 0 12px 18px 2px rgb(34 0 51 / 4%),
0 6px 22px 4px rgba(7, 48, 114, 0.12), 0 6px 22px 4px rgb(7 48 114 / 12%), 0 6px 10px -4px rgb(14 13 26 / 12%);
0 6px 10px -4px rgba(14, 13, 26, 0.12);
} }
} }

View File

@ -1,6 +1,5 @@
/* global DEFAULTS LIMITS WEB_UI PREFS */ /* global DEFAULTS LIMITS WEB_UI PREFS */
import 'core-js'; import 'core-js';
import 'fast-text-encoding'; // MS Edge support
import 'intl-pluralrules'; import 'intl-pluralrules';
import choo from 'choo'; import choo from 'choo';
import nanotiming from 'nanotiming'; import nanotiming from 'nanotiming';

View File

@ -110,7 +110,7 @@ class Storage {
} }
set user(info) { set user(info) {
return this.engine.setItem('user', JSON.stringify(info)); this.engine.setItem('user', JSON.stringify(info));
} }
getFileById(id) { getFileById(id) {

View File

@ -1,5 +1,3 @@
/* global TransformStream */
export function transformStream(readable, transformer, oncancel) { export function transformStream(readable, transformer, oncancel) {
try { try {
return readable.pipeThrough(new TransformStream(transformer)); return readable.pipeThrough(new TransformStream(transformer));

View File

@ -83,13 +83,13 @@ class Account extends Component {
<input <input
type="image" type="image"
alt="${user.email}" alt="${user.email}"
class="w-8 h-8 rounded-full border text-primary md:text-white focus:outline" class="w-8 h-8 rounded-full border-default text-primary md:text-white focus:outline"
src="${user.avatar}" src="${user.avatar}"
onclick="${e => this.avatarClick(e)}" onclick="${e => this.avatarClick(e)}"
/> />
<ul <ul
id="accountMenu" id="accountMenu"
class="invisible absolute top-0 right-0 mt-10 pt-2 pb-2 bg-white shadow-md whitespace-no-wrap outline-none z-50 dark:bg-grey-80" class="invisible absolute top-0 right-0 mt-10 pt-2 pb-2 bg-white shadow-md whitespace-nowrap outline-none z-50 dark:bg-grey-80"
onblur="${e => this.hideMenu(e)}" onblur="${e => this.hideMenu(e)}"
> >
<li class="p-2 text-grey-60 dark:text-grey-50">${user.email}</li> <li class="p-2 text-grey-60 dark:text-grey-50">${user.email}</li>

View File

@ -53,7 +53,7 @@ function password(state) {
id="password-input" id="password-input"
class="${state.archive.password class="${state.archive.password
? '' ? ''
: 'invisible'} border rounded focus:border-primary leading-normal my-1 py-1 px-2 h-8 dark:bg-grey-80" : 'invisible'} border-default rounded-default focus:border-primary leading-normal my-1 py-1 px-2 h-8 dark:bg-grey-80"
autocomplete="off" autocomplete="off"
maxlength="${MAX_LENGTH}" maxlength="${MAX_LENGTH}"
type="password" type="password"
@ -261,7 +261,7 @@ module.exports = function(state, emit, archive) {
return html` return html`
<send-archive <send-archive
id="archive-${archive.id}" id="archive-${archive.id}"
class="flex flex-col items-start rounded shadow-light bg-white p-4 w-full dark:bg-grey-90 dark:border dark:border-grey-70" class="flex flex-col items-start rounded-default shadow-light bg-white p-4 w-full dark:bg-grey-90 dark:border-default dark:border-grey-70"
> >
${archiveInfo( ${archiveInfo(
archive, archive,
@ -335,7 +335,7 @@ module.exports.wip = function(state, emit) {
fileInfo(f, remove(f, state.translate('deleteButtonHover'))) fileInfo(f, remove(f, state.translate('deleteButtonHover')))
), ),
'flex-shrink bg-grey-10 rounded-t overflow-y-auto px-6 py-4 md:h-full md:max-h-half-screen dark:bg-black', 'flex-shrink bg-grey-10 rounded-t overflow-y-auto px-6 py-4 md:h-full md:max-h-half-screen dark:bg-black',
'bg-white px-2 my-2 shadow-light rounded dark:bg-grey-90 dark:border dark:border-grey-80' 'bg-white px-2 my-2 shadow-light rounded-default dark:bg-grey-90 dark:border-default dark:border-grey-80'
)} )}
<div <div
class="flex-shrink-0 flex-grow flex items-end p-4 bg-grey-10 rounded-b mb-1 font-medium dark:bg-grey-90" class="flex-shrink-0 flex-grow flex items-end p-4 bg-grey-10 rounded-b mb-1 font-medium dark:bg-grey-90"
@ -438,7 +438,7 @@ module.exports.uploading = function(state, emit) {
return html` return html`
<send-upload-area <send-upload-area
id="${archive.id}" id="${archive.id}"
class="flex flex-col items-start rounded shadow-light bg-white p-4 w-full dark:bg-grey-90" class="flex flex-col items-start rounded-default shadow-light bg-white p-4 w-full dark:bg-grey-90"
> >
${archiveInfo(archive)} ${archiveInfo(archive)}
<div class="text-xs opacity-75 w-full mt-2 mb-2"> <div class="text-xs opacity-75 w-full mt-2 mb-2">
@ -488,7 +488,7 @@ module.exports.empty = function(state, emit) {
`; `;
return html` return html`
<send-upload-area <send-upload-area
class="flex flex-col items-center justify-center border-2 border-dashed border-grey-transparent rounded px-6 py-16 h-full w-full dark:border-grey-60" class="flex flex-col items-center justify-center border-2 border-dashed border-grey-transparent rounded-default px-6 py-16 h-full w-full dark:border-grey-60"
onclick="${e => { onclick="${e => {
if (e.target.tagName !== 'LABEL') { if (e.target.tagName !== 'LABEL') {
document.getElementById('file-upload').click(); document.getElementById('file-upload').click();
@ -563,7 +563,7 @@ module.exports.preview = function(state, emit) {
<send-archive <send-archive
class="flex flex-col max-h-full bg-white p-4 w-full md:w-128 dark:bg-grey-90" class="flex flex-col max-h-full bg-white p-4 w-full md:w-128 dark:bg-grey-90"
> >
<div class="border rounded py-3 px-6 dark:border-grey-70"> <div class="border-default rounded-default py-3 px-6 dark:border-grey-70">
${archiveInfo(archive)} ${details} ${archiveInfo(archive)} ${details}
</div> </div>
<button <button
@ -590,7 +590,7 @@ module.exports.downloading = function(state) {
const progressPercent = percent(progress); const progressPercent = percent(progress);
return html` return html`
<send-archive <send-archive
class="flex flex-col bg-white rounded shadow-light p-4 w-full max-w-sm md:w-128 dark:bg-grey-90" class="flex flex-col bg-white rounded-default shadow-light p-4 w-full max-w-sm md:w-128 dark:bg-grey-90"
> >
${archiveInfo(archive)} ${archiveInfo(archive)}
<div class="link-primary text-sm font-medium mt-2"> <div class="link-primary text-sm font-medium mt-2">

View File

@ -21,7 +21,7 @@ module.exports = function(name, url) {
<input <input
type="text" type="text"
id="share-url" id="share-url"
class="block w-full my-4 border rounded-lg leading-loose h-12 px-2 py-1 dark:bg-grey-80" class="block w-full my-4 border-default rounded-lg leading-loose h-12 px-2 py-1 dark:bg-grey-80"
value="${url}" value="${url}"
readonly="true" readonly="true"
/> />

View File

@ -17,7 +17,7 @@ module.exports = function(state, emit) {
${state.translate('downloadDescription')} ${state.translate('downloadDescription')}
</p> </p>
<form <form
class="flex flex-row flex-no-wrap w-full md:w-4/5" class="flex flex-row flex-nowrap w-full md:w-4/5"
onsubmit="${checkPassword}" onsubmit="${checkPassword}"
data-no-csrf data-no-csrf
> >

View File

@ -12,12 +12,12 @@ module.exports = function(state, emit) {
'downloadTitle' 'downloadTitle'
)}</h1> )}</h1>
<p <p
class="w-full p-2 border border-yellow-50 rounded md:w-4/5 text-orange-60 bg-yellow-40 text-center leading-normal" class="w-full p-2 border-default border-yellow-50 rounded-default md:w-4/5 text-orange-60 bg-yellow-40 text-center leading-normal"
> >
${state.translate('noStreamsWarning')} ${state.translate('noStreamsWarning')}
</p> </p>
<form class="md:w-128" onsubmit=${submit}> <form class="md:w-128" onsubmit=${submit}>
<fieldset class="border rounded p-4 my-4" onchange=${optionChanged}> <fieldset class="border-default rounded-default p-4 my-4" onchange=${optionChanged}>
<div class="flex items-center mb-2"> <div class="flex items-center mb-2">
<svg class="h-8 w-6 mr-3 flex-shrink-0 text-primary"> <svg class="h-8 w-6 mr-3 flex-shrink-0 text-primary">
<use xlink:href="${assets.get('blue_file.svg')}#icon"/> <use xlink:href="${assets.get('blue_file.svg')}#icon"/>

View File

@ -18,7 +18,7 @@ module.exports = function(selected, options, translate, changed, htmlId) {
return html` return html`
<select <select
id="${htmlId}" id="${htmlId}"
class="appearance-none cursor-pointer border rounded bg-grey-10 hover:border-primary focus:border-primary pl-1 pr-8 py-1 my-1 h-8 dark:bg-grey-80" class="appearance-none cursor-pointer border-default rounded-default bg-grey-10 hover:border-primary focus:border-primary pl-1 pr-8 py-1 my-1 h-8 dark:bg-grey-80"
data-selected="${selected}" data-selected="${selected}"
onchange="${choose}" onchange="${choose}"
> >

View File

@ -18,7 +18,7 @@ module.exports = function(name, url) {
<input <input
type="text" type="text"
id="share-url" id="share-url"
class="w-full my-4 border rounded-lg leading-loose h-12 px-2 py-1 dark:bg-grey-80" class="w-full my-4 border-default rounded-lg leading-loose h-12 px-2 py-1 dark:bg-grey-80"
value="${url}" value="${url}"
readonly="true" readonly="true"
/> />

View File

@ -35,7 +35,7 @@ module.exports = function() {
<input <input
id="email-input" id="email-input"
type="email" type="email"
class="hidden border rounded-lg w-full px-2 py-1 h-12 mb-3 text-lg text-grey-70 leading-loose dark:bg-grey-80 dark:text-white" class="hidden border-default rounded-lg w-full px-2 py-1 h-12 mb-3 text-lg text-grey-70 leading-loose dark:bg-grey-80 dark:text-white"
placeholder=${state.translate('emailPlaceholder')} placeholder=${state.translate('emailPlaceholder')}
/> />
<input <input

View File

@ -27,7 +27,7 @@ module.exports = function(state, emit) {
<main class="main"> <main class="main">
${state.modal && modal(state, emit)} ${state.modal && modal(state, emit)}
<section <section
class="flex flex-col items-center justify-center text-center bg-white m-6 px-6 py-8 border border-grey-30 md:border-none md:px-12 md:py-16 shadow w-full md:h-full dark:bg-grey-90" class="flex flex-col items-center justify-center text-center bg-white m-6 px-6 py-8 border-default border-grey-30 md:border-none md:px-12 md:py-16 shadow-default w-full md:h-full dark:bg-grey-90"
> >
<h1 class="text-3xl font-bold">${strings.header}</h1> <h1 class="text-3xl font-bold">${strings.header}</h1>
<p class="mt-4 mb-8 max-w-md leading-normal">${strings.description}</p> <p class="mt-4 mb-8 max-w-md leading-normal">${strings.description}</p>

View File

@ -23,21 +23,15 @@ function locale() {
return document.querySelector('html').lang; return document.querySelector('html').lang;
} }
function loadShim(polyfill) {
return new Promise((resolve, reject) => {
const shim = document.createElement('script');
shim.src = polyfill;
shim.addEventListener('load', () => resolve(true));
shim.addEventListener('error', () => resolve(false));
document.head.appendChild(shim);
});
}
function isFile(id) { function isFile(id) {
return /^[0-9a-fA-F]{10,16}$/.test(id); return /^[0-9a-fA-F]{10,16}$/.test(id);
} }
function copyToClipboard(str) { async function copyToClipboard(str) {
try {
await navigator.clipboard.writeText(str);
} catch {
// Older browsers or the clipboard API fails because of a missing permission
const aux = document.createElement('input'); const aux = document.createElement('input');
aux.setAttribute('value', str); aux.setAttribute('value', str);
aux.contentEditable = true; aux.contentEditable = true;
@ -56,6 +50,7 @@ function copyToClipboard(str) {
const result = document.execCommand('copy'); const result = document.execCommand('copy');
document.body.removeChild(aux); document.body.removeChild(aux);
return result; return result;
}
} }
const LOCALIZE_NUMBERS = !!( const LOCALIZE_NUMBERS = !!(
@ -287,7 +282,6 @@ module.exports = {
copyToClipboard, copyToClipboard,
arrayToB64, arrayToB64,
b64ToArray, b64ToArray,
loadShim,
isFile, isFile,
openLinksInNewTab, openLinksInNewTab,
browserName, browserName,

View File

@ -3,7 +3,7 @@ const isServer = typeof genmap === 'function';
let prefix = ''; let prefix = '';
let manifest = {}; let manifest = {};
try { try {
//eslint-disable-next-line node/no-missing-require //eslint-disable-next-line n/no-missing-require
manifest = require('../dist/manifest.json'); manifest = require('../dist/manifest.json');
} catch (e) { } catch (e) {
// use middleware // use middleware

View File

@ -6,8 +6,6 @@
- https://github.com/whatwg/streams/tree/master/reference-implementation - https://github.com/whatwg/streams/tree/master/reference-implementation
- Examples - Examples
- https://github.com/mdn/dom-examples/tree/master/streams - https://github.com/mdn/dom-examples/tree/master/streams
- Polyfill
- https://github.com/MattiasBuelens/web-streams-polyfill
# Encrypted Content Encoding # Encrypted Content Encoding

View File

@ -1,4 +1,4 @@
/* global window, document, fetch */ /* global window, document */
const MAXFILESIZE = 1024 * 1024 * 1024 * 2; const MAXFILESIZE = 1024 * 1024 * 1024 * 2;

27240
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -66,34 +66,26 @@
"@babel/plugin-proposal-class-properties": "^7.16.7", "@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-env": "^7.16.11", "@babel/preset-env": "^7.16.11",
"@dannycoates/webcrypto-liner": "^0.1.37", "@fullhuman/postcss-purgecss": "^4.1.3",
"@fullhuman/postcss-purgecss": "^1.3.0",
"@mattiasbuelens/web-streams-polyfill": "0.2.1",
"@sentry/browser": "^5.30.0", "@sentry/browser": "^5.30.0",
"asmcrypto.js": "^0.22.0",
"babel-loader": "^8.2.4", "babel-loader": "^8.2.4",
"babel-plugin-istanbul": "^5.2.0", "babel-plugin-istanbul": "^6.1.1",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"content-disposition": "^0.5.4", "content-disposition": "^0.5.4",
"copy-webpack-plugin": "^5.1.2", "copy-webpack-plugin": "^6.4.0",
"core-js": "^3.21.1", "core-js": "^3.21.1",
"crc": "^3.8.0", "crc": "^3.8.0",
"cross-env": "^6.0.3", "cross-env": "^6.0.3",
"css-loader": "^3.6.0", "css-loader": "^5.2.7",
"css-mqpacker": "^7.0.0", "cssnano": "^5.1.12",
"cssnano": "^4.1.11", "eslint": "^8.21.0",
"eslint": "^6.6.0", "eslint-config-prettier": "^8.5.0",
"eslint-config-prettier": "^6.15.0", "eslint-plugin-mocha": "^10.1.0",
"eslint-plugin-mocha": "^6.2.1", "eslint-plugin-n": "^15.2.4",
"eslint-plugin-node": "^10.0.0", "eslint-plugin-security": "^1.5.0",
"eslint-plugin-security": "^1.4.0",
"expose-loader": "^0.7.5",
"extract-loader": "^3.2.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"fast-text-encoding": "^1.0.3", "file-loader": "^6.2.0",
"file-loader": "^4.2.0", "git-rev-sync": "^3.0.2",
"git-rev-sync": "^1.12.0",
"html-loader": "^0.5.5",
"http_ece": "^1.1.0", "http_ece": "^1.1.0",
"husky": "^3.0.9", "husky": "^3.0.9",
"intl-pluralrules": "^1.3.1", "intl-pluralrules": "^1.3.1",
@ -105,23 +97,23 @@
"nanotiming": "^7.3.1", "nanotiming": "^7.3.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"nyc": "^14.1.1", "nyc": "^14.1.1",
"postcss-loader": "^3.0.0", "postcss": "^8.4.14",
"postcss-preset-env": "^6.7.1", "postcss-loader": "^4.2.0",
"postcss-preset-env": "^7.7.2",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"proxyquire": "^2.1.3", "proxyquire": "^2.1.3",
"puppeteer": "^2.0.0", "puppeteer": "^2.0.0",
"raw-loader": "^3.1.0", "raw-loader": "^3.1.0",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"script-loader": "^0.7.2",
"sinon": "^7.5.0", "sinon": "^7.5.0",
"string-hash": "^1.1.3", "string-hash": "^1.1.3",
"stylelint": "^13.13.1", "stylelint": "^14.9.1",
"stylelint-config-standard": "^19.0.0", "stylelint-config-standard": "^26.0.0",
"stylelint-no-unsupported-browser-features": "^4.1.4", "stylelint-no-unsupported-browser-features": "^5.0.3",
"svgo": "^1.3.2", "svgo": "^2.8.0",
"svgo-loader": "^2.2.2", "svgo-loader": "^3.0.1",
"tailwindcss": "^1.9.6", "tailwindcss": "^2",
"val-loader": "^1.1.1", "val-loader": "^2.1.2",
"webpack": "4.38.0", "webpack": "4.38.0",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-middleware": "^3.7.3", "webpack-dev-middleware": "^3.7.3",
@ -131,22 +123,19 @@
}, },
"dependencies": { "dependencies": {
"@dannycoates/express-ws": "^5.0.3", "@dannycoates/express-ws": "^5.0.3",
"@fluent/bundle": "^0.13.0", "@fluent/bundle": "^0.17.1",
"@fluent/langneg": "^0.3.0", "@fluent/langneg": "^0.6.2",
"@google-cloud/storage": "^5.19.0", "@google-cloud/storage": "^6.2.3",
"@sentry/node": "^5.30.0", "@sentry/node": "^7.7.0",
"aws-sdk": "^2.1109.0", "aws-sdk": "^2.1109.0",
"body-parser": "^1.20.0", "body-parser": "^1.20.0",
"choo": "^7.0.0", "choo": "^7.0.0",
"cldr-core": "^35.1.0", "cldr-core": "^35.1.0",
"configstore": "github:dannycoates/configstore#master",
"convict": "^6.2.3", "convict": "^6.2.3",
"convict-format-with-validator": "^6.2.0", "convict-format-with-validator": "^6.2.0",
"double-ended-queue": "^2.1.0-0",
"express": "^4.17.3", "express": "^4.17.3",
"helmet": "^3.23.3", "helmet": "^3.23.3",
"mkdirp": "^0.5.6", "mozlog": "^3.0.1",
"mozlog": "^2.2.0",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"redis": "^3.1.1", "redis": "^3.1.1",
"redis-mock": "^0.47.0", "redis-mock": "^0.47.0",

View File

@ -1,8 +1,6 @@
class TailwindExtractor { const TailwindExtractor = content => {
static extract(content) {
return content.match(/[A-Za-z0-9-_:/]+/g) || []; return content.match(/[A-Za-z0-9-_:/]+/g) || [];
} };
}
const options = { const options = {
plugins: [ plugins: [

View File

@ -1,5 +1,5 @@
rules: rules:
node/shebang: off n/shebang: off
security/detect-child-process: off security/detect-child-process: off
no-console: off no-console: off

View File

@ -5,7 +5,7 @@ const clientConstants = require('./clientConstants');
let sentry = ''; let sentry = '';
if (config.sentry_id) { if (config.sentry_id) {
//eslint-disable-next-line node/no-missing-require //eslint-disable-next-line n/no-missing-require
const version = require('../dist/version.json'); const version = require('../dist/version.json');
sentry = ` sentry = `
var SENTRY_CONFIG = { var SENTRY_CONFIG = {

View File

@ -1,13 +1,15 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const { FluentBundle } = require('@fluent/bundle'); const { FluentBundle, FluentResource } = require('@fluent/bundle');
const localesPath = path.resolve(__dirname, '../public/locales'); const localesPath = path.resolve(__dirname, '../public/locales');
const locales = fs.readdirSync(localesPath); const locales = fs.readdirSync(localesPath);
function makeBundle(locale) { function makeBundle(locale) {
const bundle = new FluentBundle(locale, { useIsolating: false }); const bundle = new FluentBundle(locale, { useIsolating: false });
bundle.addMessages( bundle.addResource(
new FluentResource(
fs.readFileSync(path.resolve(localesPath, locale, 'send.ftl'), 'utf8') fs.readFileSync(path.resolve(localesPath, locale, 'send.ftl'), 'utf8')
)
); );
return [locale, bundle]; return [locale, bundle];
} }
@ -19,8 +21,11 @@ module.exports = function getTranslator(locale) {
const bundle = bundles.get(locale) || defaultBundle; const bundle = bundles.get(locale) || defaultBundle;
return function(id, data) { return function(id, data) {
if (bundle.hasMessage(id)) { if (bundle.hasMessage(id)) {
return bundle.format(bundle.getMessage(id), data); return bundle.formatPattern(bundle.getMessage(id).value, data);
} }
return defaultBundle.format(defaultBundle.getMessage(id), data); return defaultBundle.formatPattern(
defaultBundle.getMessage(id).value,
data
);
}; };
}; };

View File

@ -120,7 +120,7 @@ module.exports = function(app) {
); );
app.post(`/api/info/:id${ID_REGEX}`, auth.owner, require('./info')); app.post(`/api/info/:id${ID_REGEX}`, auth.owner, require('./info'));
app.get('/__version__', function(req, res) { app.get('/__version__', function(req, res) {
// eslint-disable-next-line node/no-missing-require // eslint-disable-next-line n/no-missing-require
res.sendFile(require.resolve('../../dist/version.json')); res.sendFile(require.resolve('../../dist/version.json'));
}); });

View File

@ -1,7 +1,6 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const promisify = require('util').promisify; const promisify = require('util').promisify;
const mkdirp = require('mkdirp');
const stat = promisify(fs.stat); const stat = promisify(fs.stat);
@ -9,7 +8,9 @@ class FSStorage {
constructor(config, log) { constructor(config, log) {
this.log = log; this.log = log;
this.dir = config.file_dir; this.dir = config.file_dir;
mkdirp.sync(this.dir); fs.mkdirSync(this.dir, {
recursive: true
});
} }
async length(id) { async length(id) {

View File

@ -23,6 +23,7 @@ const colors = {
}; };
module.exports = { module.exports = {
purge: false,
theme: { theme: {
colors: colors, colors: colors,
screens: { screens: {

View File

@ -6,10 +6,10 @@ extends:
plugins: plugins:
- mocha - mocha
- node - n
rules: rules:
node/no-unpublished-require: off n/no-unpublished-require: off
mocha/handle-done-callback: error mocha/handle-done-callback: error
mocha/no-exclusive-tests: error mocha/no-exclusive-tests: error

View File

@ -5,4 +5,4 @@ parserOptions:
sourceType: module sourceType: module
rules: rules:
node/no-unsupported-features: off n/no-unsupported-features: off

View File

@ -9,7 +9,7 @@ module.exports = function() {
const files = fs const files = fs
.readdirSync(path.join(__dirname, 'tests')) .readdirSync(path.join(__dirname, 'tests'))
.filter(p => /\.js$/.test(p)); .filter(p => /\.js$/.test(p));
const code = "require('fast-text-encoding');\n" + files.map(kv).join(';\n'); const code = files.map(kv).join(';\n');
return { return {
code, code,
dependencies: files.map(f => require.resolve('./tests/' + f)), dependencies: files.map(f => require.resolve('./tests/' + f)),

View File

@ -1,7 +1,6 @@
/* eslint-disable no-undef, no-process-exit */ /* eslint-disable no-undef, no-process-exit */
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const mkdirp = require('mkdirp');
const puppeteer = require('puppeteer'); const puppeteer = require('puppeteer');
const webpack = require('webpack'); const webpack = require('webpack');
const config = require('../../webpack.config'); const config = require('../../webpack.config');
@ -44,7 +43,9 @@ const server = app.listen(async function() {
const coverage = await page.evaluate(() => __coverage__); const coverage = await page.evaluate(() => __coverage__);
if (coverage) { if (coverage) {
const dir = path.resolve(__dirname, '../../.nyc_output'); const dir = path.resolve(__dirname, '../../.nyc_output');
mkdirp.sync(dir); fs.mkdirSync(dir, {
recursive: true
});
fs.writeFileSync( fs.writeFileSync(
path.resolve(dir, 'frontend.json'), path.resolve(dir, 'frontend.json'),
JSON.stringify(coverage) JSON.stringify(coverage)

View File

@ -1,4 +1,4 @@
// eslint-disable-next-line node/no-extraneous-require // eslint-disable-next-line n/no-extraneous-require
const ip = require('ip'); const ip = require('ip');
const common = require('./wdio.common.conf'); const common = require('./wdio.common.conf');

View File

@ -1,9 +1,11 @@
const path = require('path'); const path = require('path');
const mkdirp = require('mkdirp'); const fs = require('fs');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
const dir = path.join(__dirname, 'integration', 'downloads'); const dir = path.join(__dirname, 'integration', 'downloads');
mkdirp.sync(dir); fs.mkdirSync(dir, {
recursive: true
});
rimraf.sync(`${dir}${path.sep}*`); rimraf.sync(`${dir}${path.sep}*`);
exports.config = { exports.config = {

View File

@ -1,4 +1,4 @@
// eslint-disable-next-line node/no-extraneous-require // eslint-disable-next-line n/no-extraneous-require
const ip = require('ip'); const ip = require('ip');
const common = require('./wdio.common.conf'); const common = require('./wdio.common.conf');
const dir = const dir =

View File

@ -1,4 +1,4 @@
// eslint-disable-next-line node/no-extraneous-require // eslint-disable-next-line n/no-extraneous-require
const ip = require('ip'); const ip = require('ip');
const common = require('./wdio.common.conf'); const common = require('./wdio.common.conf');

View File

@ -42,7 +42,8 @@ const serviceWorker = {
test: /\.(png|jpg)$/, test: /\.(png|jpg)$/,
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: '[name].[contenthash:8].[ext]' name: '[name].[contenthash:8].[ext]',
esModule: false
} }
}, },
{ {
@ -51,16 +52,26 @@ const serviceWorker = {
{ {
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: '[name].[contenthash:8].[ext]' name: '[name].[contenthash:8].[ext]',
esModule: false
} }
}, },
{ {
loader: 'svgo-loader', loader: 'svgo-loader',
options: { options: {
plugins: [ plugins: [
{ removeViewBox: false }, // true causes stretched images {
{ convertStyleToAttrs: true }, // for CSP, no unsafe-eval name: 'removeViewBox',
{ removeTitle: true } // for smallness active: false // true causes stretched images
},
{
name: 'convertStyleToAttrs',
active: true // for CSP, no unsafe-eval
},
{
name: 'removeTitle',
active: true // for smallness
}
] ]
} }
} }
@ -100,10 +111,6 @@ const web = {
path.resolve(__dirname, 'common'), path.resolve(__dirname, 'common'),
// some dependencies need to get re-babeled because we // some dependencies need to get re-babeled because we
// have different targets than their default configs // have different targets than their default configs
path.resolve(
__dirname,
'node_modules/@dannycoates/webcrypto-liner'
),
path.resolve(__dirname, 'node_modules/@fluent'), path.resolve(__dirname, 'node_modules/@fluent'),
path.resolve(__dirname, 'node_modules/intl-pluralrules') path.resolve(__dirname, 'node_modules/intl-pluralrules')
], ],
@ -116,8 +123,7 @@ const web = {
path.resolve(__dirname, 'node_modules/crc'), path.resolve(__dirname, 'node_modules/crc'),
path.resolve(__dirname, 'node_modules/@fluent'), path.resolve(__dirname, 'node_modules/@fluent'),
path.resolve(__dirname, 'node_modules/@sentry'), path.resolve(__dirname, 'node_modules/@sentry'),
path.resolve(__dirname, 'node_modules/tslib'), path.resolve(__dirname, 'node_modules/tslib')
path.resolve(__dirname, 'node_modules/webcrypto-core')
], ],
loader: 'webpack-unassert-loader' loader: 'webpack-unassert-loader'
} }
@ -127,7 +133,8 @@ const web = {
test: /\.(png|jpg)$/, test: /\.(png|jpg)$/,
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: '[name].[contenthash:8].[ext]' name: '[name].[contenthash:8].[ext]',
esModule: false
} }
}, },
{ {
@ -136,17 +143,30 @@ const web = {
{ {
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: '[name].[contenthash:8].[ext]' name: '[name].[contenthash:8].[ext]',
esModule: false
} }
}, },
{ {
loader: 'svgo-loader', loader: 'svgo-loader',
options: { options: {
plugins: [ plugins: [
{ cleanupIDs: false }, {
{ removeViewBox: false }, // true causes stretched images name: 'cleanupIDs',
{ convertStyleToAttrs: true }, // for CSP, no unsafe-eval active: false
{ removeTitle: true } // for smallness },
{
name: 'removeViewBox',
active: false // true causes stretched images
},
{
name: 'convertStyleToAttrs',
active: true // for CSP, no unsafe-eval
},
{
name: 'removeTitle',
active: true // for smallness
}
] ]
} }
} }
@ -160,7 +180,8 @@ const web = {
{ {
loader: 'css-loader', loader: 'css-loader',
options: { options: {
importLoaders: 1 importLoaders: 1,
esModule: false
} }
}, },
'postcss-loader' 'postcss-loader'
@ -184,12 +205,14 @@ const web = {
] ]
}, },
plugins: [ plugins: [
new CopyPlugin([ new CopyPlugin({
patterns: [
{ {
context: 'public', context: 'public',
from: '*.*' from: '*.*'
} }
]), ]
}),
new webpack.EnvironmentPlugin(['NODE_ENV']), new webpack.EnvironmentPlugin(['NODE_ENV']),
new webpack.IgnorePlugin(/\.\.\/dist/), // used in common/*.js new webpack.IgnorePlugin(/\.\.\/dist/), // used in common/*.js
new ExtractTextPlugin({ new ExtractTextPlugin({