gatsby-theme-q3 3.2.0 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +11 -0
- package/gatsby-browser.js +5 -0
- package/gatsby-config.js +7 -18
- package/gatsby-node.js +0 -1
- package/lib/components/PageWrapper.js +1 -2
- package/lib/components/SearchEngine.js +7 -10
- package/lib/components/Wrapper.js +2 -19
- package/package.json +4 -4
- package/src/components/PageWrapper.jsx +1 -5
- package/src/components/SearchEngine.jsx +10 -14
- package/src/components/Wrapper.jsx +3 -21
- package/__tests__/config.int.test.js +0 -62
- package/helpers/__tests__/loadContent.unit.test.js +0 -13
- package/helpers/__tests__/pagination.unit.test.js +0 -139
- package/helpers/__tests__/slug.unit.test.js +0 -21
- package/helpers/archive.js +0 -42
- package/helpers/index.js +0 -19
- package/helpers/loadContent.js +0 -45
- package/helpers/loadTheme.js +0 -10
- package/helpers/pagination.js +0 -109
- package/helpers/setup.js +0 -60
- package/helpers/slug.js +0 -31
- package/helpers/slugType.js +0 -24
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,17 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
+
## [3.2.1](https://github.com/3merge/q/compare/v3.2.0...v3.2.1) (2022-02-09)
|
7
|
+
|
8
|
+
|
9
|
+
### Bug Fixes
|
10
|
+
|
11
|
+
* misc domain issues ([17cb74b](https://github.com/3merge/q/commit/17cb74b927721f11b5fcd8f8a9a1a28a86867048))
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
6
17
|
# [3.2.0](https://github.com/3merge/q/compare/v3.1.5...v3.2.0) (2022-02-09)
|
7
18
|
|
8
19
|
|
package/gatsby-browser.js
CHANGED
@@ -11,6 +11,11 @@ export {
|
|
11
11
|
export const onClientEntry = async () => {
|
12
12
|
if (!browser.isBrowserReady()) return;
|
13
13
|
|
14
|
+
// set api default root
|
15
|
+
axios.defaults.baseURL =
|
16
|
+
process.env.GATSBY_APP_BASE_URL ||
|
17
|
+
'http://localhost:9000';
|
18
|
+
|
14
19
|
// set language default
|
15
20
|
axios.defaults.headers['Content-Language'] =
|
16
21
|
window.localStorage.getItem('q3-locale') || 'en';
|
package/gatsby-config.js
CHANGED
@@ -4,14 +4,7 @@ require('dotenv').config();
|
|
4
4
|
const genKey = (url) =>
|
5
5
|
String(url).includes('netlify') ? 'disallow' : 'allow';
|
6
6
|
|
7
|
-
module.exports = ({
|
8
|
-
siteUrl,
|
9
|
-
title,
|
10
|
-
brandingColor,
|
11
|
-
icon,
|
12
|
-
netlify,
|
13
|
-
...options
|
14
|
-
}) => {
|
7
|
+
module.exports = (options) => {
|
15
8
|
const plugins = [
|
16
9
|
{
|
17
10
|
resolve: 'gatsby-theme-q3-mui',
|
@@ -48,24 +41,20 @@ module.exports = ({
|
|
48
41
|
},
|
49
42
|
},
|
50
43
|
},
|
51
|
-
|
52
|
-
|
53
|
-
if (netlify)
|
54
|
-
plugins.push({
|
44
|
+
{
|
55
45
|
resolve: 'gatsby-plugin-netlify',
|
56
46
|
options: {
|
57
47
|
generateMatchPathRewrites: true,
|
58
48
|
},
|
59
|
-
}
|
60
|
-
|
61
|
-
if (siteUrl)
|
62
|
-
plugins.push({
|
49
|
+
},
|
50
|
+
{
|
63
51
|
resolve: 'gatsby-plugin-canonical-urls',
|
64
52
|
options: {
|
65
53
|
stripQueryString: true,
|
66
|
-
siteUrl,
|
54
|
+
siteUrl: process.env.URL,
|
67
55
|
},
|
68
|
-
}
|
56
|
+
},
|
57
|
+
];
|
69
58
|
|
70
59
|
return {
|
71
60
|
plugins,
|
package/gatsby-node.js
CHANGED
@@ -17,8 +17,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
17
17
|
|
18
18
|
const PageWrapper = ({
|
19
19
|
children,
|
20
|
-
includeLoader
|
21
|
-
includeLocale
|
20
|
+
includeLoader
|
22
21
|
}) => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_SearchEngine.default, null), includeLoader && /*#__PURE__*/_react.default.createElement(_components.Loader, null), children);
|
23
22
|
|
24
23
|
PageWrapper.defaultProps = {
|
@@ -3,7 +3,7 @@
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
4
4
|
value: true
|
5
5
|
});
|
6
|
-
exports.getStartUrl = exports.generateMetaTitleOptions = exports.generateMetaDescriptionOptions = exports.generateManifest = exports.generateIcons = exports.generateBrand = exports.default =
|
6
|
+
exports.getStartUrl = exports.generateMetaTitleOptions = exports.generateMetaDescriptionOptions = exports.generateManifest = exports.generateIcons = exports.generateBrand = exports.default = void 0;
|
7
7
|
|
8
8
|
var _react = _interopRequireDefault(require("react"));
|
9
9
|
|
@@ -69,13 +69,6 @@ const generateManifest = (site = {}) => ({
|
|
69
69
|
|
70
70
|
exports.generateManifest = generateManifest;
|
71
71
|
|
72
|
-
const Manifest = props => (0, _lodash.isObject)(props) ? /*#__PURE__*/_react.default.createElement("link", {
|
73
|
-
rel: "manifest",
|
74
|
-
href: `data:application/manifest+json,${encodeURIComponent(JSON.stringify(props))}`
|
75
|
-
}) : null;
|
76
|
-
|
77
|
-
exports.Manifest = Manifest;
|
78
|
-
|
79
72
|
const SEO = ({
|
80
73
|
description,
|
81
74
|
lang,
|
@@ -85,12 +78,13 @@ const SEO = ({
|
|
85
78
|
const site = (0, _useSiteMetaData.default)();
|
86
79
|
const metaDescription = description || site.description;
|
87
80
|
const metaTitle = title || site.title;
|
81
|
+
const manifestData = generateManifest(site);
|
88
82
|
return /*#__PURE__*/_react.default.createElement(_reactHelmet.Helmet, {
|
89
83
|
htmlAttributes: {
|
90
84
|
lang
|
91
85
|
},
|
92
86
|
title: metaTitle,
|
93
|
-
titleTemplate: generateBrand(
|
87
|
+
titleTemplate: generateBrand(site.brand),
|
94
88
|
meta: [...generateMetaTitleOptions(metaTitle), ...generateMetaDescriptionOptions(metaDescription), {
|
95
89
|
property: 'og:type',
|
96
90
|
content: 'website'
|
@@ -98,7 +92,10 @@ const SEO = ({
|
|
98
92
|
name: 'twitter:card',
|
99
93
|
content: 'summary'
|
100
94
|
}].concat(meta)
|
101
|
-
},
|
95
|
+
}, (0, _lodash.isObject)(manifestData) ? /*#__PURE__*/_react.default.createElement("link", {
|
96
|
+
rel: "manifest",
|
97
|
+
href: `data:application/manifest+json,${encodeURIComponent(JSON.stringify(manifestData))}`
|
98
|
+
}) : null, /*#__PURE__*/_react.default.createElement("link", {
|
102
99
|
rel: "icon",
|
103
100
|
href: site.favicon
|
104
101
|
}));
|
@@ -7,8 +7,6 @@ exports.default = void 0;
|
|
7
7
|
|
8
8
|
var _react = _interopRequireDefault(require("react"));
|
9
9
|
|
10
|
-
var _axios = _interopRequireDefault(require("axios"));
|
11
|
-
|
12
10
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
13
11
|
|
14
12
|
var _q3UiPermissions = _interopRequireDefault(require("q3-ui-permissions"));
|
@@ -16,27 +14,12 @@ var _q3UiPermissions = _interopRequireDefault(require("q3-ui-permissions"));
|
|
16
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
17
15
|
|
18
16
|
/* eslint-disable import/no-extraneous-dependencies */
|
19
|
-
const setBaseUrlForRest = (baseURL = process.env.GATSBY_APP_BASE_URL || 'http://localhost:9000') => {
|
20
|
-
_axios.default.defaults.baseURL = baseURL;
|
21
|
-
return _axios.default.defaults;
|
22
|
-
};
|
23
|
-
|
24
17
|
const Wrapper = ({
|
25
|
-
baseURL,
|
26
18
|
children
|
27
|
-
}) =>
|
28
|
-
setBaseUrlForRest(baseURL);
|
29
|
-
return /*#__PURE__*/_react.default.createElement(_q3UiPermissions.default, null, children);
|
30
|
-
};
|
19
|
+
}) => /*#__PURE__*/_react.default.createElement(_q3UiPermissions.default, null, children);
|
31
20
|
|
32
|
-
Wrapper.defaultProps = {
|
33
|
-
baseURL: undefined
|
34
|
-
};
|
35
21
|
Wrapper.propTypes = {
|
36
|
-
|
37
|
-
children: _propTypes.default.node.isRequired,
|
38
|
-
// eslint-disable-next-line
|
39
|
-
locale: _propTypes.default.object.isRequired
|
22
|
+
children: _propTypes.default.node.isRequired
|
40
23
|
};
|
41
24
|
var _default = Wrapper;
|
42
25
|
exports.default = _default;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "gatsby-theme-q3",
|
3
|
-
"version": "3.2.
|
3
|
+
"version": "3.2.1",
|
4
4
|
"main": "index.js",
|
5
5
|
"license": "MIT",
|
6
6
|
"peerDependencies": {
|
@@ -28,13 +28,13 @@
|
|
28
28
|
"gatsby-plugin-robots-txt": "^1.6.14",
|
29
29
|
"gatsby-plugin-sharp": "^4.2.0",
|
30
30
|
"gatsby-plugin-sitemap": "^5.2.0",
|
31
|
-
"gatsby-theme-q3-mui": "^3.2.
|
31
|
+
"gatsby-theme-q3-mui": "^3.2.1",
|
32
32
|
"gatsby-transformer-sharp": "^4.2.0",
|
33
33
|
"lodash": "^4.17.20",
|
34
34
|
"process": "^0.11.10",
|
35
35
|
"prop-types": "^15.7.2",
|
36
36
|
"q3-ui-helpers": "^3.2.0",
|
37
|
-
"q3-ui-locale": "^3.2.
|
37
|
+
"q3-ui-locale": "^3.2.1",
|
38
38
|
"query-string": "^7.0.1",
|
39
39
|
"react-helmet": "^6.1.0",
|
40
40
|
"react-share": "^4.3.1",
|
@@ -42,5 +42,5 @@
|
|
42
42
|
"transform-loader": "^0.2.4",
|
43
43
|
"yarn": "^1.22.17"
|
44
44
|
},
|
45
|
-
"gitHead": "
|
45
|
+
"gitHead": "b9ee55eb96e7d61f86f9d5d091d8a9d770c720cc"
|
46
46
|
}
|
@@ -3,11 +3,7 @@ import PropTypes from 'prop-types';
|
|
3
3
|
import { Loader } from 'q3-admin/lib/components';
|
4
4
|
import SearchEngine from './SearchEngine';
|
5
5
|
|
6
|
-
const PageWrapper = ({
|
7
|
-
children,
|
8
|
-
includeLoader,
|
9
|
-
includeLocale,
|
10
|
-
}) => (
|
6
|
+
const PageWrapper = ({ children, includeLoader }) => (
|
11
7
|
<>
|
12
8
|
<SearchEngine />
|
13
9
|
{includeLoader && <Loader />}
|
@@ -68,20 +68,11 @@ export const generateManifest = (site = {}) => ({
|
|
68
68
|
theme_color: site.color,
|
69
69
|
});
|
70
70
|
|
71
|
-
export const Manifest = (props) =>
|
72
|
-
isObject(props) ? (
|
73
|
-
<link
|
74
|
-
rel="manifest"
|
75
|
-
href={`data:application/manifest+json,${encodeURIComponent(
|
76
|
-
JSON.stringify(props),
|
77
|
-
)}`}
|
78
|
-
/>
|
79
|
-
) : null;
|
80
|
-
|
81
71
|
const SEO = ({ description, lang, meta, title }) => {
|
82
72
|
const site = useSiteMetaData();
|
83
73
|
const metaDescription = description || site.description;
|
84
74
|
const metaTitle = title || site.title;
|
75
|
+
const manifestData = generateManifest(site);
|
85
76
|
|
86
77
|
return (
|
87
78
|
<Helmet
|
@@ -89,9 +80,7 @@ const SEO = ({ description, lang, meta, title }) => {
|
|
89
80
|
lang,
|
90
81
|
}}
|
91
82
|
title={metaTitle}
|
92
|
-
titleTemplate={generateBrand(
|
93
|
-
get(site, 'brand', 'Q3'),
|
94
|
-
)}
|
83
|
+
titleTemplate={generateBrand(site.brand)}
|
95
84
|
meta={[
|
96
85
|
...generateMetaTitleOptions(metaTitle),
|
97
86
|
...generateMetaDescriptionOptions(metaDescription),
|
@@ -105,7 +94,14 @@ const SEO = ({ description, lang, meta, title }) => {
|
|
105
94
|
},
|
106
95
|
].concat(meta)}
|
107
96
|
>
|
108
|
-
|
97
|
+
{isObject(manifestData) ? (
|
98
|
+
<link
|
99
|
+
rel="manifest"
|
100
|
+
href={`data:application/manifest+json,${encodeURIComponent(
|
101
|
+
JSON.stringify(manifestData),
|
102
|
+
)}`}
|
103
|
+
/>
|
104
|
+
) : null}
|
109
105
|
<link rel="icon" href={site.favicon} />
|
110
106
|
</Helmet>
|
111
107
|
);
|
@@ -1,32 +1,14 @@
|
|
1
1
|
/* eslint-disable import/no-extraneous-dependencies */
|
2
2
|
import React from 'react';
|
3
|
-
import axios from 'axios';
|
4
3
|
import PropTypes from 'prop-types';
|
5
4
|
import AuthProvider from 'q3-ui-permissions';
|
6
5
|
|
7
|
-
const
|
8
|
-
|
9
|
-
|
10
|
-
) => {
|
11
|
-
axios.defaults.baseURL = baseURL;
|
12
|
-
return axios.defaults;
|
13
|
-
};
|
14
|
-
|
15
|
-
const Wrapper = ({ baseURL, children }) => {
|
16
|
-
setBaseUrlForRest(baseURL);
|
17
|
-
return <AuthProvider>{children}</AuthProvider>;
|
18
|
-
};
|
19
|
-
|
20
|
-
Wrapper.defaultProps = {
|
21
|
-
baseURL: undefined,
|
22
|
-
};
|
6
|
+
const Wrapper = ({ children }) => (
|
7
|
+
<AuthProvider>{children}</AuthProvider>
|
8
|
+
);
|
23
9
|
|
24
10
|
Wrapper.propTypes = {
|
25
|
-
baseURL: PropTypes.string,
|
26
11
|
children: PropTypes.node.isRequired,
|
27
|
-
|
28
|
-
// eslint-disable-next-line
|
29
|
-
locale: PropTypes.object.isRequired,
|
30
12
|
};
|
31
13
|
|
32
14
|
export default Wrapper;
|
@@ -1,62 +0,0 @@
|
|
1
|
-
import { get } from 'lodash';
|
2
|
-
import config from '../gatsby-config';
|
3
|
-
|
4
|
-
const CANONICAL = 'gatsby-plugin-canonical-urls';
|
5
|
-
const ROBOTS = 'gatsby-plugin-robots-txt';
|
6
|
-
|
7
|
-
const ENV = {
|
8
|
-
contentfulSpaceID: 1,
|
9
|
-
contentfulAccessToken: 1,
|
10
|
-
};
|
11
|
-
|
12
|
-
const containsResolver = (plugins = [], name) =>
|
13
|
-
plugins.find(
|
14
|
-
(p) => typeof p === 'object' && p.resolve === name,
|
15
|
-
);
|
16
|
-
|
17
|
-
const checkPlugins = (args = {}, plugin) => {
|
18
|
-
const { plugins } = config({ ...ENV, ...args });
|
19
|
-
const statement = expect(
|
20
|
-
containsResolver(plugins, plugin),
|
21
|
-
);
|
22
|
-
|
23
|
-
return {
|
24
|
-
has: () => statement.not.toBeUndefined(),
|
25
|
-
hasNot: () => statement.toBeUndefined(),
|
26
|
-
};
|
27
|
-
};
|
28
|
-
|
29
|
-
describe('gatsby-config', () => {
|
30
|
-
describe('plugins', () => {
|
31
|
-
it('should include conditional plugins', () =>
|
32
|
-
[CANONICAL].forEach((name) =>
|
33
|
-
checkPlugins(
|
34
|
-
{
|
35
|
-
brandingColor: '#FFF',
|
36
|
-
title: 'Foo',
|
37
|
-
siteUrl: 'https://google.ca',
|
38
|
-
},
|
39
|
-
name,
|
40
|
-
).has(),
|
41
|
-
));
|
42
|
-
|
43
|
-
it('should exclude conditional plugins', () =>
|
44
|
-
[CANONICAL].forEach((name) =>
|
45
|
-
checkPlugins({}, name).hasNot(),
|
46
|
-
));
|
47
|
-
|
48
|
-
it.each([
|
49
|
-
['https://dev.netlify.3merge.com', 'disallow'],
|
50
|
-
['https://3merge.com', 'allow'],
|
51
|
-
])('should disable indexing', (url, key) => {
|
52
|
-
process.env.URL = url;
|
53
|
-
const { plugins } = config({ ...ENV });
|
54
|
-
const res = containsResolver(plugins, ROBOTS);
|
55
|
-
const prod = get(
|
56
|
-
res,
|
57
|
-
'options.env.production.policy',
|
58
|
-
)[0];
|
59
|
-
expect(prod).toHaveProperty(key, '/');
|
60
|
-
});
|
61
|
-
});
|
62
|
-
});
|
@@ -1,13 +0,0 @@
|
|
1
|
-
const path = require('path');
|
2
|
-
const loadContent = require('../loadContent');
|
3
|
-
|
4
|
-
describe('loadContent', () => {
|
5
|
-
it('should fetch all content from directory', () => {
|
6
|
-
const out = loadContent(
|
7
|
-
path.resolve(__dirname, '../../__fixtures__'),
|
8
|
-
);
|
9
|
-
|
10
|
-
expect(out).toHaveProperty('en');
|
11
|
-
expect(out).toHaveProperty('fr');
|
12
|
-
});
|
13
|
-
});
|
@@ -1,139 +0,0 @@
|
|
1
|
-
const {
|
2
|
-
genCursor,
|
3
|
-
appendSiblingsToContext,
|
4
|
-
getPreviousArchiveUrl,
|
5
|
-
getNextArchiveUrl,
|
6
|
-
getNumberOfPages,
|
7
|
-
paginateArchiveContext,
|
8
|
-
} = require('../pagination');
|
9
|
-
|
10
|
-
const genEntries = () => {
|
11
|
-
const entries = [];
|
12
|
-
for (let i = 0; i < 30; i += 1) entries.push(i);
|
13
|
-
return entries;
|
14
|
-
};
|
15
|
-
|
16
|
-
describe('pagination', () => {
|
17
|
-
describe('"genCursor"', () => {
|
18
|
-
const stub = ['foo', 'bar', 'quuz', 'garply'];
|
19
|
-
const cursor = genCursor(stub, 2);
|
20
|
-
// current index targets "quuz"
|
21
|
-
|
22
|
-
it('should identify first item', () => {
|
23
|
-
expect(cursor.first).toMatch('foo');
|
24
|
-
});
|
25
|
-
|
26
|
-
it('should identify last item', () => {
|
27
|
-
expect(cursor.last).toMatch('garply');
|
28
|
-
});
|
29
|
-
|
30
|
-
it('should identify next item', () => {
|
31
|
-
expect(cursor.next).toMatch('garply');
|
32
|
-
});
|
33
|
-
|
34
|
-
it('should identify previous item', () => {
|
35
|
-
expect(cursor.prev).toMatch('bar');
|
36
|
-
});
|
37
|
-
|
38
|
-
it('should identify first position', () => {
|
39
|
-
expect(cursor.isFirst).toBeFalsy();
|
40
|
-
expect(genCursor(stub, 0).isFirst).toBeTruthy();
|
41
|
-
});
|
42
|
-
|
43
|
-
it('should identify last position', () => {
|
44
|
-
expect(cursor.isLast).toBeFalsy();
|
45
|
-
expect(genCursor(stub, 3).isLast).toBeTruthy();
|
46
|
-
});
|
47
|
-
});
|
48
|
-
|
49
|
-
describe('"appendSiblingsToContext"', () => {
|
50
|
-
const mockContentfulEntry = (id) => ({
|
51
|
-
contentful_id: id,
|
52
|
-
});
|
53
|
-
|
54
|
-
const stubWithContentful = [
|
55
|
-
mockContentfulEntry(1),
|
56
|
-
mockContentfulEntry(2),
|
57
|
-
mockContentfulEntry(3),
|
58
|
-
];
|
59
|
-
|
60
|
-
it('should map contentful entries using cursor', () => {
|
61
|
-
const entries = appendSiblingsToContext(
|
62
|
-
stubWithContentful,
|
63
|
-
);
|
64
|
-
expect(entries[0]).toMatchObject({
|
65
|
-
prev: 3,
|
66
|
-
next: 2,
|
67
|
-
});
|
68
|
-
expect(entries[2]).toMatchObject({
|
69
|
-
prev: 2,
|
70
|
-
next: 1,
|
71
|
-
});
|
72
|
-
});
|
73
|
-
});
|
74
|
-
|
75
|
-
describe('"getPreviousArchiveUrl"', () => {
|
76
|
-
it('should return null', () => {
|
77
|
-
expect(getPreviousArchiveUrl('/foo', 1)).toBeNull();
|
78
|
-
});
|
79
|
-
|
80
|
-
it('should return archive', () => {
|
81
|
-
expect(getPreviousArchiveUrl('/foo', 2)).toEqual(
|
82
|
-
'/foo',
|
83
|
-
);
|
84
|
-
});
|
85
|
-
|
86
|
-
it('should return archive sub-directory', () => {
|
87
|
-
expect(getPreviousArchiveUrl('/foo', 3)).toEqual(
|
88
|
-
'/foo/2',
|
89
|
-
);
|
90
|
-
});
|
91
|
-
});
|
92
|
-
|
93
|
-
describe('"getNextArchiveUrl"', () => {
|
94
|
-
it('should return sub-directory', () => {
|
95
|
-
expect(getNextArchiveUrl('/foo', 8, 9)).toEqual(
|
96
|
-
'/foo/9',
|
97
|
-
);
|
98
|
-
});
|
99
|
-
|
100
|
-
it('should return null', () => {
|
101
|
-
expect(getNextArchiveUrl('/foo', 9, 9)).toBeNull();
|
102
|
-
});
|
103
|
-
});
|
104
|
-
|
105
|
-
describe('"getNumberOfPages"', () => {
|
106
|
-
it('should return number divisible by', () => {
|
107
|
-
expect(getNumberOfPages(genEntries(), 5)).toBe(6);
|
108
|
-
});
|
109
|
-
});
|
110
|
-
|
111
|
-
describe('"paginateArchiveContext"', () => {
|
112
|
-
it('should return pagination meta', () => {
|
113
|
-
// default 15 per page
|
114
|
-
const res = paginateArchiveContext(
|
115
|
-
genEntries(),
|
116
|
-
'/foo',
|
117
|
-
);
|
118
|
-
|
119
|
-
expect(res).toHaveLength(2);
|
120
|
-
expect(res[0]).toMatchObject({
|
121
|
-
path: '/foo',
|
122
|
-
limit: 15,
|
123
|
-
skip: 0,
|
124
|
-
pageNum: 0,
|
125
|
-
prev: null,
|
126
|
-
next: '/foo/2',
|
127
|
-
});
|
128
|
-
|
129
|
-
expect(res[1]).toMatchObject({
|
130
|
-
path: '/foo/2',
|
131
|
-
limit: 15,
|
132
|
-
skip: 15,
|
133
|
-
pageNum: 1,
|
134
|
-
prev: '/foo',
|
135
|
-
next: null,
|
136
|
-
});
|
137
|
-
});
|
138
|
-
});
|
139
|
-
});
|
@@ -1,21 +0,0 @@
|
|
1
|
-
const slug = require('../slug');
|
2
|
-
|
3
|
-
describe('slug', () => {
|
4
|
-
it('should combine use slug attribute', () => {
|
5
|
-
expect(
|
6
|
-
slug({ slug: 'already-formatted-as-slug' }),
|
7
|
-
).toMatch('/already-formatted-as-slug');
|
8
|
-
});
|
9
|
-
|
10
|
-
it('should combine use title attribute', () => {
|
11
|
-
expect(
|
12
|
-
slug({ title: 'This is a post' }, 'foos'),
|
13
|
-
).toMatch('/foos/this-is-a-post');
|
14
|
-
});
|
15
|
-
|
16
|
-
it('should combine use name attribute', () => {
|
17
|
-
expect(slug({ name: "Post's name" }, '/foos')).toMatch(
|
18
|
-
'/foos/posts-name',
|
19
|
-
);
|
20
|
-
});
|
21
|
-
});
|
package/helpers/archive.js
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
const { get } = require('lodash');
|
2
|
-
const { resolve } = require('path');
|
3
|
-
const {
|
4
|
-
appendSiblingsToContext,
|
5
|
-
paginateArchiveContext,
|
6
|
-
} = require('./pagination');
|
7
|
-
|
8
|
-
module.exports = ({
|
9
|
-
archiveComponentRelativePath,
|
10
|
-
createPage,
|
11
|
-
detailComponentRelativePath,
|
12
|
-
nodesKeyName,
|
13
|
-
slug,
|
14
|
-
}) => async ({ data, errors }) => {
|
15
|
-
if (errors) throw errors;
|
16
|
-
const { nodes = [] } = get(data, nodesKeyName, {
|
17
|
-
nodes: [],
|
18
|
-
});
|
19
|
-
|
20
|
-
const archives = appendSiblingsToContext(nodes).map(
|
21
|
-
(context) =>
|
22
|
-
createPage({
|
23
|
-
path:
|
24
|
-
// see slugType for more details on this field
|
25
|
-
context.to || `/${slug}/${context.contentful_id}`,
|
26
|
-
component: resolve(detailComponentRelativePath),
|
27
|
-
context,
|
28
|
-
}),
|
29
|
-
);
|
30
|
-
|
31
|
-
const entries = paginateArchiveContext(nodes, slug).map(
|
32
|
-
({ path, ...context }) =>
|
33
|
-
createPage({
|
34
|
-
component: resolve(archiveComponentRelativePath),
|
35
|
-
path,
|
36
|
-
context,
|
37
|
-
}),
|
38
|
-
);
|
39
|
-
|
40
|
-
await Promise.all(archives);
|
41
|
-
await Promise.all(entries);
|
42
|
-
};
|
package/helpers/index.js
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
const ArchiveBuilder = require('./archive');
|
2
|
-
const loadContent = require('./loadContent');
|
3
|
-
const {
|
4
|
-
appendSiblingsToContext,
|
5
|
-
paginateArchiveContext,
|
6
|
-
} = require('./pagination');
|
7
|
-
const slug = require('./slug');
|
8
|
-
const slugType = require('./slugType');
|
9
|
-
const setup = require('./setup');
|
10
|
-
|
11
|
-
module.exports = {
|
12
|
-
ArchiveBuilder,
|
13
|
-
loadContent,
|
14
|
-
appendSiblingsToContext,
|
15
|
-
paginateArchiveContext,
|
16
|
-
setup,
|
17
|
-
slug,
|
18
|
-
slugType,
|
19
|
-
};
|
package/helpers/loadContent.js
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
const fs = require('fs');
|
2
|
-
const path = require('path');
|
3
|
-
|
4
|
-
const readJsonFile = (dir, filename) => {
|
5
|
-
try {
|
6
|
-
const file = path.resolve(dir, filename);
|
7
|
-
const buffer = fs.readFileSync(file);
|
8
|
-
return JSON.parse(buffer);
|
9
|
-
} catch (e) {
|
10
|
-
return {};
|
11
|
-
}
|
12
|
-
};
|
13
|
-
|
14
|
-
const reduceFileSystem = (name, next) =>
|
15
|
-
fs
|
16
|
-
.readdirSync(name, { withFileTypes: true })
|
17
|
-
.reduce(next, {});
|
18
|
-
|
19
|
-
const getJsonFileNameFromDirent = ({ name }) =>
|
20
|
-
path.basename(name, '.json');
|
21
|
-
|
22
|
-
const readFilePathFromDirent = ({ name }, root, next) =>
|
23
|
-
next(path.join(root, name));
|
24
|
-
|
25
|
-
const recurseFileSystem = (pathName) =>
|
26
|
-
reduceFileSystem(pathName, (curr, dirent) =>
|
27
|
-
Object.assign(curr, {
|
28
|
-
[getJsonFileNameFromDirent(
|
29
|
-
dirent,
|
30
|
-
)]: dirent.isDirectory()
|
31
|
-
? readFilePathFromDirent(
|
32
|
-
dirent,
|
33
|
-
pathName,
|
34
|
-
recurseFileSystem,
|
35
|
-
)
|
36
|
-
: readJsonFile(pathName, dirent.name),
|
37
|
-
}),
|
38
|
-
);
|
39
|
-
|
40
|
-
recurseFileSystem.readJsonFile = readJsonFile;
|
41
|
-
recurseFileSystem.reduceFileSystem = reduceFileSystem;
|
42
|
-
recurseFileSystem.getJsonFileNameFromDirent = getJsonFileNameFromDirent;
|
43
|
-
recurseFileSystem.readFilePathFromDirent = readFilePathFromDirent;
|
44
|
-
|
45
|
-
module.exports = recurseFileSystem;
|
package/helpers/loadTheme.js
DELETED
package/helpers/pagination.js
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
const { get } = require('lodash');
|
2
|
-
|
3
|
-
const getId = (v) => get(v, 'contentful_id');
|
4
|
-
|
5
|
-
const genCursor = (a = [], i = 0) => ({
|
6
|
-
isFirst: i === 0,
|
7
|
-
isLast: i === a.length - 1,
|
8
|
-
next: a[i + 1],
|
9
|
-
prev: a[i - 1],
|
10
|
-
first: a[0],
|
11
|
-
last: a[a.length - 1],
|
12
|
-
});
|
13
|
-
|
14
|
-
const joinArchiveUrlWithPageNumber = (url, page) =>
|
15
|
-
`${url}/${page}`;
|
16
|
-
|
17
|
-
const getPreviousArchiveUrl = (archiveUrl, page) => {
|
18
|
-
if (page === 2) return archiveUrl;
|
19
|
-
if (page !== 1)
|
20
|
-
return joinArchiveUrlWithPageNumber(
|
21
|
-
archiveUrl,
|
22
|
-
page - 1,
|
23
|
-
);
|
24
|
-
|
25
|
-
return null;
|
26
|
-
};
|
27
|
-
|
28
|
-
const getNextArchiveUrl = (
|
29
|
-
archiveUrl,
|
30
|
-
page,
|
31
|
-
totalNumberOfPages,
|
32
|
-
) => {
|
33
|
-
if (page < totalNumberOfPages)
|
34
|
-
return joinArchiveUrlWithPageNumber(
|
35
|
-
archiveUrl,
|
36
|
-
page + 1,
|
37
|
-
);
|
38
|
-
|
39
|
-
return null;
|
40
|
-
};
|
41
|
-
|
42
|
-
const getCurrentArchiveUrl = (archiveUrl, page) =>
|
43
|
-
page < 2
|
44
|
-
? archiveUrl
|
45
|
-
: joinArchiveUrlWithPageNumber(archiveUrl, page);
|
46
|
-
|
47
|
-
const getNumberOfPages = (
|
48
|
-
entries = [],
|
49
|
-
postsPerPage = 15,
|
50
|
-
) => {
|
51
|
-
const len = Array.isArray(entries) ? entries.length : 0;
|
52
|
-
return Math.ceil(len / postsPerPage);
|
53
|
-
};
|
54
|
-
|
55
|
-
const appendSiblingsToContext = (entries) =>
|
56
|
-
entries.map((node, i) => {
|
57
|
-
const cursor = genCursor(entries, i);
|
58
|
-
const prev = getId(
|
59
|
-
cursor.isFirst ? cursor.last : cursor.prev,
|
60
|
-
);
|
61
|
-
|
62
|
-
const next = getId(
|
63
|
-
cursor.isLast ? cursor.first : cursor.next,
|
64
|
-
);
|
65
|
-
|
66
|
-
return {
|
67
|
-
...node,
|
68
|
-
prev,
|
69
|
-
next,
|
70
|
-
};
|
71
|
-
});
|
72
|
-
|
73
|
-
const paginateArchiveContext = (entries = [], pathName) => {
|
74
|
-
const postsPerPage = 15;
|
75
|
-
const numPages = getNumberOfPages(entries, postsPerPage);
|
76
|
-
const output = [];
|
77
|
-
|
78
|
-
for (let i = 0; i < numPages; i += 1) {
|
79
|
-
const page = i + 1; // always offset for pretty URLs
|
80
|
-
const path = getCurrentArchiveUrl(pathName, page);
|
81
|
-
const prev = getPreviousArchiveUrl(pathName, page);
|
82
|
-
const next = getNextArchiveUrl(
|
83
|
-
pathName,
|
84
|
-
page,
|
85
|
-
numPages,
|
86
|
-
);
|
87
|
-
|
88
|
-
output.push({
|
89
|
-
limit: postsPerPage,
|
90
|
-
skip: i * postsPerPage,
|
91
|
-
total: numPages,
|
92
|
-
pageNum: i,
|
93
|
-
path,
|
94
|
-
next,
|
95
|
-
prev,
|
96
|
-
});
|
97
|
-
}
|
98
|
-
|
99
|
-
return output;
|
100
|
-
};
|
101
|
-
|
102
|
-
module.exports = {
|
103
|
-
appendSiblingsToContext,
|
104
|
-
genCursor,
|
105
|
-
getNextArchiveUrl,
|
106
|
-
getNumberOfPages,
|
107
|
-
getPreviousArchiveUrl,
|
108
|
-
paginateArchiveContext,
|
109
|
-
};
|
package/helpers/setup.js
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
const path = require('path');
|
2
|
-
const fs = require('fs');
|
3
|
-
const { compact, get } = require('lodash');
|
4
|
-
const loadContent = require('./loadContent');
|
5
|
-
const loadTheme = require('./loadTheme');
|
6
|
-
|
7
|
-
const getFile =
|
8
|
-
(directory) =>
|
9
|
-
(possibleFileNames = []) =>
|
10
|
-
possibleFileNames.reduce((acc, curr) => {
|
11
|
-
if (acc) return acc;
|
12
|
-
const filename = path.resolve(directory, curr);
|
13
|
-
return fs.existsSync(filename) ? filename : undefined;
|
14
|
-
}, undefined);
|
15
|
-
|
16
|
-
module.exports = (
|
17
|
-
siteMetadata,
|
18
|
-
plugins = [],
|
19
|
-
workingDirection = process.cwd(),
|
20
|
-
) => {
|
21
|
-
const f = getFile(workingDirection);
|
22
|
-
|
23
|
-
const locale = loadContent(f(['locale', 'lang']));
|
24
|
-
const theme = loadTheme(
|
25
|
-
f(['theme.js', 'gatsby-theme.js', 'mui.js']),
|
26
|
-
);
|
27
|
-
|
28
|
-
return {
|
29
|
-
siteMetadata: {
|
30
|
-
appDirectory: '/app',
|
31
|
-
author: '3merge',
|
32
|
-
description: '',
|
33
|
-
siteUrl: 'https://3merge.ca/',
|
34
|
-
...siteMetadata,
|
35
|
-
},
|
36
|
-
plugins: compact(
|
37
|
-
[
|
38
|
-
{
|
39
|
-
resolve: 'gatsby-theme-q3',
|
40
|
-
options: {
|
41
|
-
icon: f([
|
42
|
-
'static/favicon.png',
|
43
|
-
'static/favicon.jpg',
|
44
|
-
]),
|
45
|
-
|
46
|
-
brandingColor: get(
|
47
|
-
theme,
|
48
|
-
'palette.primary.main',
|
49
|
-
'#000',
|
50
|
-
),
|
51
|
-
|
52
|
-
locale,
|
53
|
-
theme,
|
54
|
-
...siteMetadata,
|
55
|
-
},
|
56
|
-
},
|
57
|
-
].concat(plugins),
|
58
|
-
),
|
59
|
-
};
|
60
|
-
};
|
package/helpers/slug.js
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
const slugify = require('slugify');
|
2
|
-
|
3
|
-
const getSlug = (target) => {
|
4
|
-
const keys = ['slug', 'title', 'name', 'contentful_id'];
|
5
|
-
|
6
|
-
let slug;
|
7
|
-
let i = 0;
|
8
|
-
|
9
|
-
do {
|
10
|
-
const v = target[keys[i]];
|
11
|
-
if (v)
|
12
|
-
slug = slugify(v, {
|
13
|
-
replacement: '-',
|
14
|
-
remove: undefined,
|
15
|
-
lower: true,
|
16
|
-
strict: true,
|
17
|
-
});
|
18
|
-
|
19
|
-
i += 1;
|
20
|
-
} while (!slug);
|
21
|
-
|
22
|
-
return slug;
|
23
|
-
};
|
24
|
-
|
25
|
-
const getDirectoryPath = (v) => {
|
26
|
-
if (typeof v !== 'string') return '/';
|
27
|
-
return v.startsWith('/') ? v : `/${v}`;
|
28
|
-
};
|
29
|
-
|
30
|
-
module.exports = (node = {}, basepath = '/') =>
|
31
|
-
[getDirectoryPath(basepath), getSlug(node)].join('/');
|
package/helpers/slugType.js
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
const slug = require('./slug');
|
2
|
-
|
3
|
-
module.exports = (
|
4
|
-
dir,
|
5
|
-
resourceName,
|
6
|
-
{ createFieldExtension, createTypes },
|
7
|
-
) => {
|
8
|
-
// mirrors reach router prop
|
9
|
-
// an unlikely name otherwise
|
10
|
-
const resolverKey = 'to';
|
11
|
-
|
12
|
-
createFieldExtension({
|
13
|
-
name: resolverKey,
|
14
|
-
extend: () => ({
|
15
|
-
resolve: (source) => slug(source, dir),
|
16
|
-
}),
|
17
|
-
});
|
18
|
-
|
19
|
-
createTypes(`
|
20
|
-
type ${resourceName} implements Node {
|
21
|
-
${resolverKey}: String @${resolverKey}
|
22
|
-
}
|
23
|
-
`);
|
24
|
-
};
|