@financial-times/dotcom-ui-shell 7.2.7 → 7.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/node/components/Content.d.ts +6 -0
  2. package/dist/node/components/DocumentHead.d.ts +36 -0
  3. package/dist/node/components/GTMBody.d.ts +11 -0
  4. package/dist/node/components/GTMHead.d.ts +11 -0
  5. package/dist/node/components/LinkedData.d.ts +13 -0
  6. package/dist/node/components/OpenGraph.d.ts +12 -0
  7. package/dist/node/components/ResourceHints.d.ts +11 -0
  8. package/dist/node/components/Shell.d.ts +29 -0
  9. package/dist/node/components/StyleSheets.d.ts +14 -0
  10. package/dist/node/index.d.ts +1 -0
  11. package/dist/node/lib/flattenOpenGraphData.d.ts +4 -0
  12. package/dist/node/lib/formatAttributeNames.d.ts +4 -0
  13. package/dist/node/lib/getResourceType.d.ts +2 -0
  14. package/dist/node/lib/imageServiceIconURL.d.ts +2 -0
  15. package/dist/node/lib/loadAsyncStylesheets.d.ts +2 -0
  16. package/dist/tsconfig.tsbuildinfo +2370 -0
  17. package/package.json +11 -9
  18. package/src/components/Content.tsx +0 -25
  19. package/src/components/DocumentHead.tsx +0 -103
  20. package/src/components/GTMBody.tsx +0 -29
  21. package/src/components/GTMHead.tsx +0 -23
  22. package/src/components/LinkedData.tsx +0 -38
  23. package/src/components/OpenGraph.tsx +0 -20
  24. package/src/components/ResourceHints.tsx +0 -63
  25. package/src/components/Shell.tsx +0 -97
  26. package/src/components/StyleSheets.tsx +0 -35
  27. package/src/index.ts +0 -1
  28. package/src/lib/flattenOpenGraphData.ts +0 -24
  29. package/src/lib/formatAttributeNames.ts +0 -36
  30. package/src/lib/getResourceType.ts +0 -34
  31. package/src/lib/imageServiceIconURL.ts +0 -24
  32. package/src/lib/loadAsyncStylesheets.ts +0 -27
package/package.json CHANGED
@@ -1,18 +1,17 @@
1
1
  {
2
2
  "name": "@financial-times/dotcom-ui-shell",
3
- "version": "7.2.7",
3
+ "version": "7.3.1",
4
4
  "description": "",
5
5
  "main": "component.js",
6
6
  "browser": "browser.js",
7
7
  "types": "src/index.ts",
8
8
  "scripts": {
9
9
  "test": "echo \"Error: no test specified\" && exit 1",
10
- "tsc": "../../node_modules/.bin/tsc --incremental",
11
10
  "clean": "npm run clean:dist && npm run clean:node_modules",
12
11
  "clean:dist": "rm -rf dist",
13
12
  "clean:node_modules": "rm -rf node_modules",
14
13
  "clean:install": "npm run clean && npm i",
15
- "build:node": "npm run tsc -- --module commonjs --outDir ./dist/node",
14
+ "build:node": "tsc",
16
15
  "build": "npm run build:node",
17
16
  "dev": "npm run build:node -- --watch",
18
17
  "preinstall": "[ \"$INIT_CWD\" != \"$PWD\" ] || npm_config_yes=true npx check-engine"
@@ -21,11 +20,11 @@
21
20
  "author": "",
22
21
  "license": "MIT",
23
22
  "dependencies": {
24
- "@financial-times/dotcom-ui-app-context": "^7.2.7",
25
- "@financial-times/dotcom-ui-base-styles": "^7.2.7",
26
- "@financial-times/dotcom-ui-bootstrap": "^7.2.7",
27
- "@financial-times/dotcom-ui-flags": "^7.2.7",
28
- "@financial-times/dotcom-ui-polyfill-service": "^7.2.7",
23
+ "@financial-times/dotcom-ui-app-context": "file:../dotcom-ui-app-context",
24
+ "@financial-times/dotcom-ui-base-styles": "file:../dotcom-ui-base-styles",
25
+ "@financial-times/dotcom-ui-bootstrap": "file:../dotcom-ui-bootstrap",
26
+ "@financial-times/dotcom-ui-flags": "file:../dotcom-ui-flags",
27
+ "@financial-times/dotcom-ui-polyfill-service": "file:../dotcom-ui-polyfill-service",
29
28
  "mime-types": "^2.1.26"
30
29
  },
31
30
  "peerDependencies": {
@@ -35,6 +34,9 @@
35
34
  "node": ">= 14.0.0",
36
35
  "npm": "7.x || 8.x"
37
36
  },
37
+ "files": [
38
+ "dist/"
39
+ ],
38
40
  "repository": {
39
41
  "type": "git",
40
42
  "repository": "https://github.com/Financial-Times/dotcom-page-kit.git",
@@ -48,4 +50,4 @@
48
50
  "check-engine": "^1.10.1",
49
51
  "react": "^16.8.6"
50
52
  }
51
- }
53
+ }
@@ -1,25 +0,0 @@
1
- import React from 'react'
2
-
3
- export type TContentProps = {
4
- contents?: string | React.ReactNode
5
- }
6
-
7
- const styles = {
8
- display: 'contents'
9
- }
10
-
11
- function Content({ contents }: TContentProps) {
12
- if (typeof contents === 'string') {
13
- return <div style={styles} dangerouslySetInnerHTML={{ __html: contents }} />
14
- }
15
-
16
- // We could try and validate this but there are so many possibilities
17
- // of node types and potentially nested arrays etc.
18
- if (contents) {
19
- return <React.Fragment>{contents}</React.Fragment>
20
- }
21
-
22
- return null
23
- }
24
-
25
- export default Content
@@ -1,103 +0,0 @@
1
- import React from 'react'
2
- import imageServiceIconURL from '../lib/imageServiceIconURL'
3
- import OpenGraph, { TOpenGraphProps } from './OpenGraph'
4
- import LinkedData, { TLinkedDataProps } from './LinkedData'
5
-
6
- export type TDocumentHeadProps = TOpenGraphProps &
7
- TLinkedDataProps & {
8
- description?: string
9
- facebookPage?: string
10
- googleSiteVerification?: string
11
- metaTags?: Array<{ [key: string]: any }>
12
- pageTitle: string
13
- robots?: string
14
- siteTitle?: string
15
- twitterSite?: string
16
- canonicalURL?: string
17
- manifestFile?: string
18
- additionalMetadata?: React.ReactNode,
19
- showSmartBanner?: boolean
20
- }
21
-
22
- const DocumentHead = (props: TDocumentHeadProps) => (
23
- <React.Fragment>
24
- <meta charSet="utf-8" />
25
- <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
26
- <meta name="viewport" content="width=device-width, initial-scale=1" />
27
-
28
- <title>{props.pageTitle ? `${props.pageTitle} | ${props.siteTitle}` : props.siteTitle}</title>
29
-
30
- {props.description && <meta name="description" content={props.description} />}
31
-
32
- {props.canonicalURL && <link rel="canonical" href={props.canonicalURL} />}
33
-
34
- {/* SEO */}
35
- <meta name="robots" content={props.robots} />
36
- <meta name="google-site-verification" content={props.googleSiteVerification} />
37
- {props.metaTags.map((attributes, i) => (
38
- <meta key={`meta-${i}`} {...attributes} />
39
- ))}
40
- <LinkedData jsonLd={props.jsonLd} />
41
-
42
- {/* social media */}
43
- <meta property="fb:pages" content={props.facebookPage} />
44
- <meta property="twitter:site" content={props.twitterSite} />
45
- <OpenGraph openGraph={props.openGraph} />
46
-
47
- {/* native apps */}
48
- {props.showSmartBanner &&
49
- (
50
- <meta
51
- name="apple-itunes-app"
52
- content={props.canonicalURL ? `app-id=1200842933, app-argument=${props.canonicalURL}` : 'app-id=1200842933'}
53
- />
54
- )
55
- }
56
-
57
- {/* packaging */}
58
- <link
59
- rel="icon"
60
- type="image/svg+xml"
61
- href={imageServiceIconURL('ftlogo-v1:brand-ft-logo-square-coloured', 0, 'svg')}
62
- />
63
- <link
64
- rel="alternate icon"
65
- type="image/png"
66
- href={imageServiceIconURL('ftlogo-v1:brand-ft-logo-square-coloured', 32)}
67
- sizes="32x32"
68
- />
69
- <link
70
- rel="alternate icon"
71
- type="image/png"
72
- href={imageServiceIconURL('ftlogo-v1:brand-ft-logo-square-coloured', 194)}
73
- sizes="194x194"
74
- />
75
- <link
76
- rel="apple-touch-icon"
77
- href={imageServiceIconURL('ftlogo-v1:brand-ft-logo-square-coloured', 180)}
78
- sizes="180x180"
79
- />
80
-
81
- {props.manifestFile ? <link rel="manifest" href={props.manifestFile} /> : null}
82
-
83
- {/* We can't add an option for every single metadata option so allow custom elements to be inserted*/}
84
- {props.additionalMetadata}
85
- </React.Fragment>
86
- )
87
-
88
- DocumentHead.defaultProps = {
89
- description:
90
- 'News, analysis and comment from the Financial Times, the worldʼs leading global business publication',
91
- facebookPage: '8860325749',
92
- googleSiteVerification: '4-t8sFaPvpO5FH_Gnw1dkM28CQepjzo8UjjAkdDflTw',
93
- metaTags: [],
94
- jsonLd: [],
95
- robots: 'index,follow,max-snippet:200,max-image-preview:large',
96
- siteTitle: 'Financial Times',
97
- twitterSite: '@FinancialTimes',
98
- manifestFile: 'https://www.ft.com/__assets/creatives/manifest/manifest-v6.json',
99
- additionalMetadata: null,
100
- showSmartBanner: true
101
- }
102
-
103
- export default DocumentHead
@@ -1,29 +0,0 @@
1
- import React from 'react'
2
- import { TFlagsData } from '@financial-times/dotcom-ui-flags/src/types'
3
-
4
- // This component is maintained by the ads team
5
- const GTMBody = ({ flags }: { flags: TFlagsData }) => {
6
- if (!flags.enableGTM) {
7
- return null
8
- }
9
-
10
- return (
11
- <noscript>
12
- <iframe
13
- src="https://www.googletagmanager.com/ns.html?id=GTM-NWQJW68"
14
- height="0"
15
- width="0"
16
- style={{
17
- display: 'none',
18
- visibility: 'hidden'
19
- }}
20
- />
21
- </noscript>
22
- )
23
- }
24
-
25
- GTMBody.defaultProps = {
26
- flags: {}
27
- }
28
-
29
- export default GTMBody
@@ -1,23 +0,0 @@
1
- import React from 'react'
2
- import { TFlagsData } from '@financial-times/dotcom-ui-flags/src/types'
3
-
4
- // This component is maintained by the ads team
5
- const GTMHead = ({ flags }: { flags: TFlagsData }) => {
6
- if (!flags.enableGTM) {
7
- return null
8
- }
9
-
10
- const tagManager = `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
11
- new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
12
- j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
13
- 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
14
- })(window,document,'script','dataLayer','GTM-NWQJW68');`
15
-
16
- return <script dangerouslySetInnerHTML={{ __html: tagManager }} />
17
- }
18
-
19
- GTMHead.defaultProps = {
20
- flags: {}
21
- }
22
-
23
- export default GTMHead
@@ -1,38 +0,0 @@
1
- import React from 'react'
2
-
3
- export type TLinkedDataProps = {
4
- jsonLd?: { [key: string]: any }
5
- }
6
-
7
- const LinkedData = ({ jsonLd }: TLinkedDataProps) => (
8
- <React.Fragment>
9
- {Array.isArray(jsonLd) &&
10
- jsonLd.map((data, i) => (
11
- <script
12
- key={`jsonld-${i}`}
13
- type="application/ld+json"
14
- dangerouslySetInnerHTML={{
15
- __html: JSON.stringify(data)
16
- }}
17
- />
18
- ))}
19
- <script
20
- type="application/ld+json"
21
- dangerouslySetInnerHTML={{
22
- __html: JSON.stringify({
23
- '@context': 'http://schema.org',
24
- '@type': 'WebSite',
25
- name: 'Financial Times',
26
- alternateName: 'FT.com',
27
- url: 'http://www.ft.com'
28
- })
29
- }}
30
- />
31
- </React.Fragment>
32
- )
33
-
34
- LinkedData.defaultProps = {
35
- jsonLd: []
36
- }
37
-
38
- export default LinkedData
@@ -1,20 +0,0 @@
1
- import React from 'react'
2
- import flattenOpenGraphData, { TOpenGraphData } from '../lib/flattenOpenGraphData'
3
-
4
- export type TOpenGraphProps = {
5
- openGraph?: TOpenGraphData
6
- }
7
-
8
- const OpenGraph = ({ openGraph }: TOpenGraphProps) => (
9
- <React.Fragment>
10
- {flattenOpenGraphData(openGraph).map(([property, content], i) => (
11
- <meta key={`og-${i}`} property={property} content={content} />
12
- ))}
13
- </React.Fragment>
14
- )
15
-
16
- OpenGraph.defaultProps = {
17
- openGraph: {}
18
- }
19
-
20
- export default OpenGraph
@@ -1,63 +0,0 @@
1
- import React from 'react'
2
- import mimeTypes from 'mime-types'
3
- import getResourceType from '../lib/getResourceType'
4
-
5
- export type TResourceHintsProps = {
6
- resourceHints?: string[]
7
- }
8
-
9
- const ResourceHints = (props: TResourceHintsProps) => {
10
- return (
11
- <React.Fragment>
12
- {/*
13
- Spoor is the API which receives the tracking events sent by the o-tracking library
14
- <https://github.com/Financial-Times/o-tracking>
15
- */}
16
- <link rel="preconnect" href="https://spoor-api.ft.com" />
17
- {/*
18
- The session API is used to validate users and retrieve information about them
19
- <https://github.com/Financial-Times/next-session>
20
- */}
21
- <link rel="preconnect" href="https://session-next.ft.com" crossOrigin="use-credentials" />
22
- {/*
23
- The ads API is used to fetch ad targeting information for the current page
24
- <https://github.com/Financial-Times/next-ads-api>
25
- */}
26
- <link rel="preconnect" href="https://ads-api.ft.com" />
27
- {/*
28
- The Google Publisher Tag library (GPT) is hosted here which is used to deliver ads
29
- <https://github.com/Financial-Times/o-ads/blob/HEAD/src/js/ad-servers/gpt.js>
30
- */}
31
- <link rel="preconnect" href="https://securepubads.g.doubleclick.net" />
32
-
33
- {props.resourceHints.map((resource, i) => {
34
- const contentType = getResourceType(resource)
35
- const mimeType =
36
- mimeTypes.lookup(resource) ||
37
- mimeTypes.lookup(resource.match(/(?<=font_format=)([a-z0-9]+)/)?.[0]) ||
38
- null
39
-
40
- const attributes: React.LinkHTMLAttributes<HTMLLinkElement> = {
41
- as: contentType,
42
- href: resource,
43
- type: mimeType
44
- }
45
-
46
- // Fonts are expected to be fetched anonymously by the browser, and the preload request is
47
- // only made anonymous by using the crossorigin attribute.
48
- // <https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content>
49
- if (contentType === 'font') {
50
- attributes.crossOrigin = 'anonymous'
51
- }
52
-
53
- return <link key={`hint-${i}`} rel="preload" {...attributes} />
54
- })}
55
- </React.Fragment>
56
- )
57
- }
58
-
59
- ResourceHints.defaultProps = {
60
- resourceHints: []
61
- }
62
-
63
- export default ResourceHints
@@ -1,97 +0,0 @@
1
- import React from 'react'
2
- import Content, { TContentProps } from './Content'
3
- import DocumentHead, { TDocumentHeadProps } from './DocumentHead'
4
- import StyleSheets, { TStylesheetProps } from './StyleSheets'
5
- import ResourceHints, { TResourceHintsProps } from './ResourceHints'
6
- import { AppContextEmbed, TAppContextProps } from '@financial-times/dotcom-ui-app-context'
7
- import {
8
- fontFaceURLs,
9
- documentStyles,
10
- LoadFontsEmbed,
11
- loadCustomFontsClassNames
12
- } from '@financial-times/dotcom-ui-base-styles'
13
- import { FlagsEmbed, TFlagsEmbedProps } from '@financial-times/dotcom-ui-flags'
14
- import { Bootstrap, TBootstrapProps } from '@financial-times/dotcom-ui-bootstrap'
15
- import * as polyfillService from '@financial-times/dotcom-ui-polyfill-service'
16
- import formatAttributeNames, { TAttributeData } from '../lib/formatAttributeNames'
17
- import GTMHead from './GTMHead'
18
- import GTMBody from './GTMBody'
19
-
20
- type TShellProps = TDocumentHeadProps &
21
- TAppContextProps &
22
- TStylesheetProps &
23
- TResourceHintsProps &
24
- TContentProps &
25
- TFlagsEmbedProps & {
26
- scripts?: string[]
27
- children?: any
28
- initialProps?: any
29
- bodyAttributes?: TAttributeData
30
- htmlAttributes?: TAttributeData
31
- }
32
-
33
- function Shell(props: TShellProps) {
34
- const bootstrapProps: TBootstrapProps = {
35
- coreScripts: [polyfillService.core()],
36
- enhancedScripts: [polyfillService.enhanced(), ...props.scripts]
37
- }
38
-
39
- const resourceHints = [
40
- polyfillService.enhanced(),
41
- // There is no need to include stylesheets here as any <link rel="stylesheet" /> tags
42
- // should be found by the browser's speculative parser.
43
- ...props.scripts,
44
- ...props.resourceHints,
45
- ...fontFaceURLs
46
- ]
47
-
48
- return (
49
- <html
50
- {...formatAttributeNames(props.htmlAttributes)}
51
- lang="en-GB"
52
- className={`no-js core ${loadCustomFontsClassNames}`}
53
- data-o-component="o-typography"
54
- style={documentStyles}
55
- >
56
- <head>
57
- <DocumentHead {...props} />
58
- <ResourceHints resourceHints={resourceHints} />
59
- {/* TODO: refactor initial props, flags data, and context data to the bottom of body */}
60
- <script
61
- id="initial-props"
62
- type="application/json"
63
- dangerouslySetInnerHTML={{ __html: JSON.stringify(props.initialProps) }}
64
- />
65
- <StyleSheets
66
- criticalStyles={props.criticalStyles}
67
- stylesheets={props.stylesheets}
68
- asyncStylesheets={props.asyncStylesheets}
69
- />
70
- <Bootstrap {...bootstrapProps} />
71
- <GTMHead flags={props.flags} />
72
- <LoadFontsEmbed />
73
- </head>
74
- <body {...formatAttributeNames(props.bodyAttributes)}>
75
- <GTMBody flags={props.flags} />
76
- <Content contents={props.contents || props.children} />
77
- <AppContextEmbed appContext={props.appContext} />
78
- <FlagsEmbed flags={props.flags} />
79
- </body>
80
- </html>
81
- )
82
- }
83
-
84
- Shell.defaultProps = {
85
- scripts: [],
86
- stylesheets: [],
87
- asyncStylesheets: [],
88
- resourceHints: [],
89
- htmlAttributes: {},
90
- bodyAttributes: {}
91
- }
92
-
93
- export { Shell }
94
- export type { TShellProps }
95
-
96
- // Export sub-components to more-easily enable custom integrations
97
- export { DocumentHead, ResourceHints, Content }
@@ -1,35 +0,0 @@
1
- import React from 'react'
2
- import loadAsyncStylesheetsString from '../lib/loadAsyncStylesheets'
3
-
4
- export type TStylesheetProps = {
5
- criticalStyles?: string
6
- stylesheets?: string[]
7
- asyncStylesheets?: string[]
8
- }
9
-
10
- const Stylesheets = ({ criticalStyles, stylesheets, asyncStylesheets }: TStylesheetProps) => (
11
- <React.Fragment>
12
- {criticalStyles && <style dangerouslySetInnerHTML={{ __html: criticalStyles }} />}
13
- {Array.isArray(stylesheets) &&
14
- stylesheets.map((stylesheet, i) => <link rel="stylesheet" key={`stylesheet-${i}`} href={stylesheet} />)}
15
- {Array.isArray(asyncStylesheets) && !!asyncStylesheets.length && (
16
- <React.Fragment>
17
- <noscript>
18
- {asyncStylesheets.map((stylesheet, i) => (
19
- <link rel="stylesheet" href={stylesheet} key={`async-stylesheet-${i}`} />
20
- ))}
21
- </noscript>
22
- <script
23
- data-stylesheets={asyncStylesheets.join()}
24
- dangerouslySetInnerHTML={{ __html: loadAsyncStylesheetsString }}></script>
25
- </React.Fragment>
26
- )}
27
- </React.Fragment>
28
- )
29
-
30
- Stylesheets.defaultProps = {
31
- stylesheets: [],
32
- asyncStylesheets: []
33
- }
34
-
35
- export default Stylesheets
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './components/Shell'
@@ -1,24 +0,0 @@
1
- // Flattens a nested object into an array of key/value pairs
2
- // { foo: { bar: { baz: 123 } } } => [['foo:bar:baz', 123]]
3
-
4
- export type TOpenGraphData = {
5
- [key: string]: any | any[] | TOpenGraphData
6
- }
7
-
8
- export default function flattenData(data: TOpenGraphData, prefix?: string): Array<string[]> {
9
- const output = []
10
-
11
- for (const [key, value] of Object.entries(data)) {
12
- const property = prefix ? `${prefix}:${key}` : key
13
-
14
- if (value && value.constructor === Object) {
15
- output.push(...flattenData(value, property))
16
- } else if (Array.isArray(value)) {
17
- output.push(...value.map((value) => [property, value]))
18
- } else {
19
- output.push([property, value])
20
- }
21
- }
22
-
23
- return output
24
- }
@@ -1,36 +0,0 @@
1
- export type TAttributeData = {
2
- [key: string]: string | number | boolean
3
- }
4
-
5
- export default function formatAttributeNames(data: TAttributeData = {}) {
6
- const output = {}
7
-
8
- for (const [key, value] of Object.entries(data)) {
9
- const hyphenatedKey = hyphenateString(key)
10
-
11
- // Let's render boolean data attributes properly
12
- // as per https://github.com/Financial-Times/dotcom-page-kit/issues/370
13
- if (hyphenatedKey.startsWith('data-') && typeof value === 'boolean') {
14
- // Where react is concerned, a `true` boolean data attribute
15
- // is one where the attribute value is an empty string (because
16
- // it is not possible to render an attribute without a value),
17
- // and a `false` boolean data attribute is one where the attribute
18
- // has not been specified altogether
19
- if (value) {
20
- output[hyphenatedKey] = ''
21
- }
22
- } else {
23
- output[hyphenatedKey] = value
24
- }
25
- }
26
-
27
- return output
28
- }
29
-
30
- function hyphenateChar(char) {
31
- return '-' + char.toLowerCase()
32
- }
33
-
34
- function hyphenateString(prop) {
35
- return prop.replace(/([A-Z])/g, hyphenateChar)
36
- }
@@ -1,34 +0,0 @@
1
- import path from 'path'
2
- import url from 'url'
3
-
4
- const StyleFiles = new Set(['.css'])
5
-
6
- const ScriptFiles = new Set(['.js', '.mjs'])
7
-
8
- const ImageFiles = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp'])
9
-
10
- export default (file: string): string => {
11
- // Always parse the file so that we can ignore any domain names, query strings etc.
12
- // Node's old URL API is able to parse anything inc. filenames, paths, and URLs.
13
- const { pathname } = url.parse(file)
14
-
15
- const extension = path.extname(pathname)
16
-
17
- if (StyleFiles.has(extension)) {
18
- return 'style'
19
- }
20
-
21
- if (ScriptFiles.has(extension)) {
22
- return 'script'
23
- }
24
-
25
- if (ImageFiles.has(extension)) {
26
- return 'image'
27
- }
28
-
29
- if (file.includes('font_format=woff')) {
30
- return 'font'
31
- }
32
-
33
- throw Error(`Unknown filename extension "${extension}`)
34
- }
@@ -1,24 +0,0 @@
1
- import querystring from 'querystring'
2
-
3
- function imageServiceIconURL(image: string, size: number, format = 'png'): string {
4
- const serviceURL = 'https://www.ft.com/__origami/service/image/v2/images/raw/'
5
-
6
- const serviceParameters = {
7
- source: 'update-logos',
8
- format: format,
9
- width: size,
10
- height: size
11
- }
12
-
13
- // Do not add width and height if format is svg because svg files scale automatically
14
- if (format === 'svg') {
15
- delete serviceParameters.width
16
- delete serviceParameters.height
17
- }
18
-
19
- const queryString = querystring.stringify(serviceParameters)
20
-
21
- return `${serviceURL}${encodeURIComponent(image)}?${queryString}`
22
- }
23
-
24
- export default imageServiceIconURL
@@ -1,27 +0,0 @@
1
- /*
2
- Load stylesheets asyncronously. See:
3
- • https://www.filamentgroup.com/lab/load-css-simpler/
4
- • https://w3c.github.io/preload/#example-5
5
-
6
- @NOTE: This is in ES5 syntax, because it's not compiled, because it's server-side code.
7
- (You don't need to compile server-side code because you get to set whichever version of node you want.)
8
- Its stringified and given to the client via "dangerouslySetInnerHTML" in a <script> tag.
9
- Because it runs in the client, it needs to be ES5 so it's compatible with older browsers.
10
- */
11
- function loadAsyncStylesheets() {
12
- var currentScript = document.scripts[document.scripts.length - 1]
13
- var stylesheets = currentScript.getAttribute('data-stylesheets').split(',')
14
-
15
- for (var i = 0, len = stylesheets.length; i < len; i++) {
16
- var link = document.createElement('link')
17
- link.href = stylesheets[i]
18
- link.rel = 'stylesheet'
19
- link.media = 'print' // <-- 'print' is intentional; on load, it changes to 'all'.
20
- link.onload = function (event) {
21
- var target = event.target as HTMLLinkElement
22
- target.media = 'all'
23
- }
24
- currentScript.parentNode.insertBefore(link, currentScript)
25
- }
26
- }
27
- export default '(' + loadAsyncStylesheets.toString() + ')()'