diff --git a/app/components/DocumentHistory/components/Revision.js b/app/components/DocumentHistory/components/Revision.js index 2c29cb2e..f1469f89 100644 --- a/app/components/DocumentHistory/components/Revision.js +++ b/app/components/DocumentHistory/components/Revision.js @@ -1,5 +1,5 @@ // @flow -import format from "date-fns/format"; +import { format } from "date-fns"; import * as React from "react"; import { NavLink } from "react-router-dom"; import styled, { withTheme } from "styled-components"; @@ -37,7 +37,7 @@ class RevisionListItem extends React.Component { {showMenu && ( diff --git a/app/components/DocumentViews.js b/app/components/DocumentViews.js index 14352464..0d012ee9 100644 --- a/app/components/DocumentViews.js +++ b/app/components/DocumentViews.js @@ -1,5 +1,5 @@ // @flow -import distanceInWordsToNow from "date-fns/distance_in_words_to_now"; +import { formatDistanceToNow } from "date-fns"; import { sortBy } from "lodash"; import { observer } from "mobx-react"; import * as React from "react"; @@ -55,7 +55,7 @@ function DocumentViews({ document, isOpen }: Props) { ? t("Currently editing") : t("Currently viewing") : t("Viewed {{ timeAgo }} ago", { - timeAgo: distanceInWordsToNow( + timeAgo: formatDistanceToNow( view ? new Date(view.lastViewedAt) : new Date() ), }); diff --git a/app/components/LocaleTime.js b/app/components/LocaleTime.js index c872cd36..2e5f09b6 100644 --- a/app/components/LocaleTime.js +++ b/app/components/LocaleTime.js @@ -1,20 +1,21 @@ // @flow -import distanceInWordsToNow from "date-fns/distance_in_words_to_now"; -import format from "date-fns/format"; +import { format, formatDistanceToNow } from "date-fns"; +import { enUS, de, fr, es, it, ko, ptBR, pt, zhCN, ru } from "date-fns/locale"; import * as React from "react"; import Tooltip from "components/Tooltip"; import useUserLocale from "hooks/useUserLocale"; const locales = { - en: require(`date-fns/locale/en`), - de: require(`date-fns/locale/de`), - es: require(`date-fns/locale/es`), - fr: require(`date-fns/locale/fr`), - it: require(`date-fns/locale/it`), - ko: require(`date-fns/locale/ko`), - pt: require(`date-fns/locale/pt`), - zh: require(`date-fns/locale/zh_cn`), - ru: require(`date-fns/locale/ru`), + en_US: enUS, + de_DE: de, + es_ES: es, + fr_FR: fr, + it_IT: it, + ko_KR: ko, + pt_BR: ptBR, + pt_PT: pt, + zh_CN: zhCN, + ru_RU: ru, }; let callbacks = []; @@ -64,7 +65,7 @@ function LocaleTime({ }; }, []); - let content = distanceInWordsToNow(dateTime, { + let content = formatDistanceToNow(Date.parse(dateTime), { addSuffix, locale: userLocale ? locales[userLocale] : undefined, }); @@ -78,7 +79,7 @@ function LocaleTime({ return ( diff --git a/app/components/Time.js b/app/components/Time.js index 5ed0d931..96a2e5fa 100644 --- a/app/components/Time.js +++ b/app/components/Time.js @@ -1,5 +1,5 @@ // @flow -import distanceInWordsToNow from "date-fns/distance_in_words_to_now"; +import { formatDistanceToNow } from "date-fns"; import * as React from "react"; const LocaleTime = React.lazy(() => @@ -15,7 +15,7 @@ type Props = { }; function Time(props: Props) { - let content = distanceInWordsToNow(props.dateTime, { + let content = formatDistanceToNow(Date.parse(props.dateTime), { addSuffix: props.addSuffix, }); diff --git a/app/hooks/useUserLocale.js b/app/hooks/useUserLocale.js index e786dff1..bd7f4894 100644 --- a/app/hooks/useUserLocale.js +++ b/app/hooks/useUserLocale.js @@ -8,5 +8,5 @@ export default function useUserLocale() { return undefined; } - return auth.user.language.split("_")[0]; + return auth.user.language; } diff --git a/app/models/Document.js b/app/models/Document.js index c6061ff2..553106cd 100644 --- a/app/models/Document.js +++ b/app/models/Document.js @@ -1,6 +1,5 @@ // @flow -import addDays from "date-fns/add_days"; -import differenceInDays from "date-fns/difference_in_days"; +import { addDays, differenceInDays } from "date-fns"; import invariant from "invariant"; import { action, computed, observable, set } from "mobx"; import parseTitle from "shared/utils/parseTitle"; diff --git a/app/routes/authenticated.js b/app/routes/authenticated.js index 876cadcf..5bc7caa1 100644 --- a/app/routes/authenticated.js +++ b/app/routes/authenticated.js @@ -3,7 +3,6 @@ import * as React from "react"; import { Switch, Redirect, type Match } from "react-router-dom"; import Archive from "scenes/Archive"; import Collection from "scenes/Collection"; -import KeyedDocument from "scenes/Document/KeyedDocument"; import DocumentNew from "scenes/DocumentNew"; import Drafts from "scenes/Drafts"; import Error404 from "scenes/Error404"; @@ -23,7 +22,11 @@ import { matchDocumentSlug as slug } from "utils/routeHelpers"; const SettingsRoutes = React.lazy(() => import(/* webpackChunkName: "settings" */ "./settings") ); - +const KeyedDocument = React.lazy(() => + import( + /* webpackChunkName: "keyed-document" */ "scenes/Document/KeyedDocument" + ) +); const NotFound = () => ; const RedirectDocument = ({ match }: { match: Match }) => ( { // search for exact internal document const slug = parseDocumentSlug(term); try { - const { document } = await this.props.documents.fetch(slug); - const time = distanceInWordsToNow(document.updatedAt, { + const { + document, + }: { document: Document } = await this.props.documents.fetch(slug); + + const time = formatDistanceToNow(Date.parse(document.updatedAt), { addSuffix: true, }); return [ @@ -118,7 +121,7 @@ class DataLoader extends React.Component { return sortBy( results.map((document) => { - const time = distanceInWordsToNow(document.updatedAt, { + const time = formatDistanceToNow(document.updatedAt, { addSuffix: true, }); return { diff --git a/app/scenes/Document/components/SharePopover.js b/app/scenes/Document/components/SharePopover.js index 18698d87..f0500522 100644 --- a/app/scenes/Document/components/SharePopover.js +++ b/app/scenes/Document/components/SharePopover.js @@ -1,5 +1,5 @@ // @flow -import distanceInWordsToNow from "date-fns/distance_in_words_to_now"; +import { formatDistanceToNow } from "date-fns"; import invariant from "invariant"; import { observer } from "mobx-react"; import { GlobeIcon, PadlockIcon } from "outline-icons"; @@ -118,9 +118,12 @@ function SharePopover({ document, share, sharedParent, onSubmit }: Props) { <> .{" "} {t("The shared link was last accessed {{ timeAgo }}.", { - timeAgo: distanceInWordsToNow(share.lastAccessedAt, { - addSuffix: true, - }), + timeAgo: formatDistanceToNow( + Date.parse(share.lastAccessedAt), + { + addSuffix: true, + } + ), })} )} diff --git a/app/scenes/UserProfile.js b/app/scenes/UserProfile.js index 4fdcac06..c5c74ff0 100644 --- a/app/scenes/UserProfile.js +++ b/app/scenes/UserProfile.js @@ -1,5 +1,5 @@ // @flow -import distanceInWordsToNow from "date-fns/distance_in_words_to_now"; +import { formatDistanceToNow } from "date-fns"; import { observer } from "mobx-react"; import { EditIcon } from "outline-icons"; import * as React from "react"; @@ -52,7 +52,7 @@ function UserProfile(props: Props) { ? t("Joined") : t("Invited")}{" "} {t("{{ time }} ago.", { - time: distanceInWordsToNow(new Date(user.createdAt)), + time: formatDistanceToNow(new Date(user.createdAt)), })} {user.isAdmin && ( {t("Admin")} diff --git a/package.json b/package.json index 582ae525..4c462d8d 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "compressorjs": "^1.0.7", "copy-to-clipboard": "^3.0.6", "core-js": "^3.10.2", - "date-fns": "1.29.0", + "date-fns": "2.22.1", "dd-trace": "^0.32.2", "debug": "^4.1.1", "dotenv": "^4.0.0", diff --git a/server/api/attachments.js b/server/api/attachments.js index 677765bb..77143e29 100644 --- a/server/api/attachments.js +++ b/server/api/attachments.js @@ -1,5 +1,5 @@ // @flow -import format from "date-fns/format"; +import { format } from "date-fns"; import Router from "koa-router"; import { v4 as uuidv4 } from "uuid"; import { NotFoundError } from "../errors"; @@ -39,7 +39,7 @@ router.post("attachments.create", auth(), async (ctx) => { const bucket = acl === "public-read" ? "public" : "uploads"; const key = `${bucket}/${user.id}/${s3Key}/${name}`; const credential = makeCredential(); - const longDate = format(new Date(), "YYYYMMDDTHHmmss\\Z"); + const longDate = format(new Date(), "yyyyMMdd'T'HHmmss'Z'"); const policy = makePolicy(credential, longDate, acl, contentType); const endpoint = publicS3Endpoint(); const url = `${endpoint}/${key}`; diff --git a/server/api/utils.js b/server/api/utils.js index 3bd36029..81fbbb2f 100644 --- a/server/api/utils.js +++ b/server/api/utils.js @@ -1,5 +1,5 @@ // @flow -import subDays from "date-fns/sub_days"; +import { subDays } from "date-fns"; import debug from "debug"; import Router from "koa-router"; import { AuthenticationError } from "../errors"; diff --git a/server/api/utils.test.js b/server/api/utils.test.js index 56f105fe..e6d8ed10 100644 --- a/server/api/utils.test.js +++ b/server/api/utils.test.js @@ -1,5 +1,5 @@ /* eslint-disable flowtype/require-valid-file-annotation */ -import subDays from "date-fns/sub_days"; +import { subDays } from "date-fns"; import TestServer from "fetch-test-server"; import app from "../app"; import { Attachment, Document } from "../models"; diff --git a/server/auth/index.js b/server/auth/index.js index 6e08b475..b1064829 100644 --- a/server/auth/index.js +++ b/server/auth/index.js @@ -1,6 +1,6 @@ // @flow import passport from "@outlinewiki/koa-passport"; -import addMonths from "date-fns/add_months"; +import { addMonths } from "date-fns"; import debug from "debug"; import Koa from "koa"; import bodyParser from "koa-body"; diff --git a/server/auth/providers/email.js b/server/auth/providers/email.js index 4bf6cd76..fc20ff52 100644 --- a/server/auth/providers/email.js +++ b/server/auth/providers/email.js @@ -1,5 +1,5 @@ // @flow -import subMinutes from "date-fns/sub_minutes"; +import { subMinutes } from "date-fns"; import Router from "koa-router"; import { find } from "lodash"; import { AuthorizationError } from "../../errors"; diff --git a/server/models/User.js b/server/models/User.js index 4c4638c9..a07c02cb 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -1,7 +1,6 @@ // @flow import crypto from "crypto"; -import addMinutes from "date-fns/add_minutes"; -import subMinutes from "date-fns/sub_minutes"; +import { addMinutes, subMinutes } from "date-fns"; import JWT from "jsonwebtoken"; import { v4 as uuidv4 } from "uuid"; import { languages } from "../../shared/i18n"; diff --git a/server/models/View.js b/server/models/View.js index 7607aebc..45e07d65 100644 --- a/server/models/View.js +++ b/server/models/View.js @@ -1,5 +1,5 @@ // @flow -import subMilliseconds from "date-fns/sub_milliseconds"; +import { subMilliseconds } from "date-fns"; import { USER_PRESENCE_INTERVAL } from "../../shared/constants"; import { User } from "../models"; import { DataTypes, Op, sequelize } from "../sequelize"; diff --git a/server/services/websockets.js b/server/services/websockets.js index e4940f18..10f2708c 100644 --- a/server/services/websockets.js +++ b/server/services/websockets.js @@ -1,5 +1,5 @@ // @flow -import subHours from "date-fns/sub_hours"; +import { subHours } from "date-fns"; import type { Event } from "../events"; import { socketio } from "../main"; import { diff --git a/server/utils/authentication.js b/server/utils/authentication.js index ead036ec..7c9bf703 100644 --- a/server/utils/authentication.js +++ b/server/utils/authentication.js @@ -1,7 +1,7 @@ // @flow import querystring from "querystring"; import * as Sentry from "@sentry/node"; -import addMonths from "date-fns/add_months"; +import { addMonths } from "date-fns"; import { type Context } from "koa"; import { pick } from "lodash"; import { User, Event, Team } from "../models"; diff --git a/server/utils/jwt.js b/server/utils/jwt.js index 9168ab62..0a91c889 100644 --- a/server/utils/jwt.js +++ b/server/utils/jwt.js @@ -1,5 +1,5 @@ // @flow -import subMinutes from "date-fns/sub_minutes"; +import { subMinutes } from "date-fns"; import JWT from "jsonwebtoken"; import { AuthenticationError } from "../errors"; import { Team, User } from "../models"; diff --git a/server/utils/passport.js b/server/utils/passport.js index 4d94bb1a..0055f24b 100644 --- a/server/utils/passport.js +++ b/server/utils/passport.js @@ -1,6 +1,5 @@ // @flow -import addMinutes from "date-fns/add_minutes"; -import subMinutes from "date-fns/sub_minutes"; +import { addMinutes, subMinutes } from "date-fns"; import { type Request } from "koa"; import { OAuthStateMismatchError } from "../errors"; import { getCookieDomain } from "./domains"; diff --git a/server/utils/s3.js b/server/utils/s3.js index df41da0f..121be2b8 100644 --- a/server/utils/s3.js +++ b/server/utils/s3.js @@ -2,8 +2,7 @@ import crypto from "crypto"; import * as Sentry from "@sentry/node"; import AWS from "aws-sdk"; -import addHours from "date-fns/add_hours"; -import format from "date-fns/format"; +import { addHours, format } from "date-fns"; import fetch from "fetch-with-proxy"; const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY; @@ -36,7 +35,7 @@ export const makeCredential = () => { const credential = AWS_ACCESS_KEY_ID + "/" + - format(new Date(), "YYYYMMDD") + + format(new Date(), "yyyyMMdd") + "/" + AWS_REGION + "/s3/aws4_request"; @@ -62,7 +61,7 @@ export const makePolicy = ( { "x-amz-credential": credential }, { "x-amz-date": longDate }, ], - expiration: format(tomorrow, "YYYY-MM-DDTHH:mm:ss\\Z"), + expiration: format(tomorrow, "yyyy-MM-dd'T'HH:mm:ss'Z'"), }; return Buffer.from(JSON.stringify(policy)).toString("base64"); @@ -71,7 +70,7 @@ export const makePolicy = ( export const getSignature = (policy: any) => { const kDate = hmac( "AWS4" + AWS_SECRET_ACCESS_KEY, - format(new Date(), "YYYYMMDD") + format(new Date(), "yyyyMMdd") ); const kRegion = hmac(kDate, AWS_REGION); const kService = hmac(kRegion, "s3"); diff --git a/shared/i18n/index.js b/shared/i18n/index.js index 9be2fb6e..847c318e 100644 --- a/shared/i18n/index.js +++ b/shared/i18n/index.js @@ -13,6 +13,7 @@ export const languageOptions = [ { label: "Français (France)", value: "fr_FR" }, { label: "Italiano (Italia)", value: "it_IT" }, { label: "한국어 (Korean)", value: "ko_KR" }, + { label: "Português (Brazil)", value: "pt_BR" }, { label: "Português (Portugal)", value: "pt_PT" }, { label: "Pусский (Россия)", value: "ru_RU" }, ]; diff --git a/shared/utils/date.js b/shared/utils/date.js index d19518b5..1d4a6e7b 100644 --- a/shared/utils/date.js +++ b/shared/utils/date.js @@ -1,8 +1,5 @@ // @flow -import subDays from "date-fns/sub_days"; -import subMonth from "date-fns/sub_months"; -import subWeek from "date-fns/sub_weeks"; -import subYear from "date-fns/sub_years"; +import { subDays, subMonths, subWeeks, subYears } from "date-fns"; export function subtractDate( date: Date, @@ -12,11 +9,11 @@ export function subtractDate( case "day": return subDays(date, 1); case "week": - return subWeek(date, 1); + return subWeeks(date, 1); case "month": - return subMonth(date, 1); + return subMonths(date, 1); case "year": - return subYear(date, 1); + return subYears(date, 1); default: return date; } diff --git a/webpack.config.js b/webpack.config.js index f5f8785d..1987c1de 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -99,13 +99,24 @@ module.exports = { moduleIds: 'hashed', chunkIds: 'named', splitChunks: { + chunks: 'async', + minSize: 20000, + minChunks: 1, + maxAsyncRequests: 30, + maxInitialRequests: 30, + enforceSizeThreshold: 50000, cacheGroups: { - vendor: { + defaultVendors: { test: /[\\/]node_modules[\\/]/, - name: 'vendors', - chunks: 'initial', + priority: -10, + reuseExistingChunk: true, }, - }, - }, + default: { + minChunks: 2, + priority: -20, + reuseExistingChunk: true, + } + } + } } }; diff --git a/yarn.lock b/yarn.lock index 9f376812..025994c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4381,10 +4381,10 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date-fns@1.29.0: - version "1.29.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" - integrity sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw== +date-fns@2.22.1: + version "2.22.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.22.1.tgz#1e5af959831ebb1d82992bf67b765052d8f0efc4" + integrity sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg== dd-trace@^0.32.2: version "0.32.2"