refactor: convert function expressions to declarations (#4926)

This commit is contained in:
Vladislav Shkodin 2021-02-08 20:01:21 +02:00 committed by GitHub
parent c0236536dd
commit 141a2eba56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
241 changed files with 3444 additions and 2933 deletions

View File

@ -30,6 +30,7 @@ module.exports = {
'@emotion/styled-import': 'error', '@emotion/styled-import': 'error',
'require-atomic-updates': [0], 'require-atomic-updates': [0],
'object-shorthand': ['error', 'always'], 'object-shorthand': ['error', 'always'],
'func-style': ['error', 'declaration'],
'prefer-const': [ 'prefer-const': [
'error', 'error',
{ {

View File

@ -74,7 +74,7 @@ const defaultPlugins = [
], ],
]; ];
const presets = () => { function presets() {
return [ return [
'@babel/preset-react', '@babel/preset-react',
'@babel/preset-env', '@babel/preset-env',
@ -86,9 +86,9 @@ const presets = () => {
], ],
'@babel/typescript', '@babel/typescript',
]; ];
}; }
const plugins = () => { function plugins() {
if (isESM) { if (isESM) {
return [ return [
...defaultPlugins, ...defaultPlugins,
@ -129,7 +129,7 @@ const plugins = () => {
} }
return defaultPlugins; return defaultPlugins;
}; }
module.exports = { module.exports = {
presets: presets(), presets: presets(),

View File

@ -161,7 +161,7 @@ function delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
const getChangeItem = (item: AzureCommitItem) => { function getChangeItem(item: AzureCommitItem) {
switch (item.action) { switch (item.action) {
case AzureCommitChangeType.ADD: case AzureCommitChangeType.ADD:
return { return {
@ -195,7 +195,7 @@ const getChangeItem = (item: AzureCommitItem) => {
default: default:
return {}; return {};
} }
}; }
type AzureCommitItem = { type AzureCommitItem = {
action: AzureCommitChangeType; action: AzureCommitChangeType;

View File

@ -33,7 +33,7 @@ import {
const MAX_CONCURRENT_DOWNLOADS = 10; const MAX_CONCURRENT_DOWNLOADS = 10;
const parseAzureRepo = (config: Config) => { function parseAzureRepo(config: Config) {
const { repo } = config.backend; const { repo } = config.backend;
if (typeof repo !== 'string') { if (typeof repo !== 'string') {
@ -51,7 +51,7 @@ const parseAzureRepo = (config: Config) => {
project, project,
repoName, repoName,
}; };
}; }
export default class Azure implements Implementation { export default class Azure implements Implementation {
lock: AsyncLock; lock: AsyncLock;

View File

@ -186,14 +186,14 @@ export const API_NAME = 'Bitbucket';
const APPLICATION_JSON = 'application/json; charset=utf-8'; const APPLICATION_JSON = 'application/json; charset=utf-8';
const replace404WithEmptyResponse = (err: FetchError) => { function replace404WithEmptyResponse(err: FetchError) {
if (err && err.status === 404) { if (err && err.status === 404) {
console.log('This 404 was expected and handled appropriately.'); console.log('This 404 was expected and handled appropriately.');
return { size: 0, values: [] as BitBucketFile[] } as BitBucketSrcResult; return { size: 0, values: [] as BitBucketFile[] } as BitBucketSrcResult;
} else { } else {
return Promise.reject(err); return Promise.reject(err);
} }
}; }
export default class API { export default class API {
apiRoot: string; apiRoot: string;

View File

@ -121,11 +121,11 @@ interface NetlifyUser extends Credentials {
user_metadata: { full_name: string; avatar_url: string }; user_metadata: { full_name: string; avatar_url: string };
} }
const apiGet = async (path: string) => { async function apiGet(path: string) {
const apiRoot = 'https://api.netlify.com/api/v1/sites'; const apiRoot = 'https://api.netlify.com/api/v1/sites';
const response = await fetch(`${apiRoot}/${path}`).then(res => res.json()); const response = await fetch(`${apiRoot}/${path}`).then(res => res.json());
return response; return response;
}; }
export default class GitGateway implements Implementation { export default class GitGateway implements Implementation {
config: Config; config: Config;

View File

@ -15,8 +15,9 @@ type ClientConfig = {
transformImages: ImageTransformations | boolean; transformImages: ImageTransformations | boolean;
}; };
export const matchPath = ({ patterns }: ClientConfig, path: string) => export function matchPath({ patterns }: ClientConfig, path: string) {
patterns.some(pattern => minimatch(path, pattern, { matchBase: true })); return patterns.some(pattern => minimatch(path, pattern, { matchBase: true }));
}
// //
// API interactions // API interactions
@ -26,10 +27,10 @@ const defaultContentHeaders = {
['Content-Type']: 'application/vnd.git-lfs+json', ['Content-Type']: 'application/vnd.git-lfs+json',
}; };
const resourceExists = async ( async function resourceExists(
{ rootURL, makeAuthorizedRequest }: ClientConfig, { rootURL, makeAuthorizedRequest }: ClientConfig,
{ sha, size }: PointerFile, { sha, size }: PointerFile,
) => { ) {
const response = await makeAuthorizedRequest({ const response = await makeAuthorizedRequest({
url: `${rootURL}/verify`, url: `${rootURL}/verify`,
method: 'POST', method: 'POST',
@ -45,20 +46,20 @@ const resourceExists = async (
// TODO: what kind of error to throw here? APIError doesn't seem // TODO: what kind of error to throw here? APIError doesn't seem
// to fit // to fit
}; }
const getTransofrmationsParams = (t: boolean | ImageTransformations) => { function getTransofrmationsParams(t: boolean | ImageTransformations) {
if (isPlainObject(t) && !isEmpty(t)) { if (isPlainObject(t) && !isEmpty(t)) {
const { nf_resize: resize, w, h } = t as ImageTransformations; const { nf_resize: resize, w, h } = t as ImageTransformations;
return `?nf_resize=${resize}&w=${w}&h=${h}`; return `?nf_resize=${resize}&w=${w}&h=${h}`;
} }
return ''; return '';
}; }
const getDownloadURL = async ( async function getDownloadURL(
{ rootURL, transformImages: t, makeAuthorizedRequest }: ClientConfig, { rootURL, transformImages: t, makeAuthorizedRequest }: ClientConfig,
{ sha }: PointerFile, { sha }: PointerFile,
) => { ) {
try { try {
const transformation = getTransofrmationsParams(t); const transformation = getTransofrmationsParams(t);
const transformedPromise = makeAuthorizedRequest(`${rootURL}/origin/${sha}${transformation}`); const transformedPromise = makeAuthorizedRequest(`${rootURL}/origin/${sha}${transformation}`);
@ -81,21 +82,23 @@ const getDownloadURL = async (
console.error(error); console.error(error);
return { url: '', blob: new Blob() }; return { url: '', blob: new Blob() };
} }
}; }
const uploadOperation = (objects: PointerFile[]) => ({ function uploadOperation(objects: PointerFile[]) {
return {
operation: 'upload', operation: 'upload',
transfers: ['basic'], transfers: ['basic'],
objects: objects.map(({ sha, ...rest }) => ({ ...rest, oid: sha })), objects: objects.map(({ sha, ...rest }) => ({ ...rest, oid: sha })),
}); };
}
const getResourceUploadURLs = async ( async function getResourceUploadURLs(
{ {
rootURL, rootURL,
makeAuthorizedRequest, makeAuthorizedRequest,
}: { rootURL: string; makeAuthorizedRequest: MakeAuthorizedRequest }, }: { rootURL: string; makeAuthorizedRequest: MakeAuthorizedRequest },
pointerFiles: PointerFile[], pointerFiles: PointerFile[],
) => { ) {
const response = await makeAuthorizedRequest({ const response = await makeAuthorizedRequest({
url: `${rootURL}/objects/batch`, url: `${rootURL}/objects/batch`,
method: 'POST', method: 'POST',
@ -113,19 +116,20 @@ const getResourceUploadURLs = async (
}, },
); );
return uploadUrls; return uploadUrls;
}; }
const uploadBlob = (uploadURL: string, blob: Blob) => function uploadBlob(uploadURL: string, blob: Blob) {
unsentRequest.fetchWithTimeout(uploadURL, { return unsentRequest.fetchWithTimeout(uploadURL, {
method: 'PUT', method: 'PUT',
body: blob, body: blob,
}); });
}
const uploadResource = async ( async function uploadResource(
clientConfig: ClientConfig, clientConfig: ClientConfig,
{ sha, size }: PointerFile, { sha, size }: PointerFile,
resource: Blob, resource: Blob,
) => { ) {
const existingFile = await resourceExists(clientConfig, { sha, size }); const existingFile = await resourceExists(clientConfig, { sha, size });
if (existingFile) { if (existingFile) {
return sha; return sha;
@ -133,13 +137,14 @@ const uploadResource = async (
const [uploadURL] = await getResourceUploadURLs(clientConfig, [{ sha, size }]); const [uploadURL] = await getResourceUploadURLs(clientConfig, [{ sha, size }]);
await uploadBlob(uploadURL, resource); await uploadBlob(uploadURL, resource);
return sha; return sha;
}; }
// //
// Create Large Media client // Create Large Media client
const configureFn = (config: ClientConfig, fn: Function) => (...args: unknown[]) => function configureFn(config: ClientConfig, fn: Function) {
fn(config, ...args); return (...args: unknown[]) => fn(config, ...args);
}
const clientFns: Record<string, Function> = { const clientFns: Record<string, Function> = {
resourceExists, resourceExists,
@ -159,7 +164,7 @@ export type Client = {
enabled: boolean; enabled: boolean;
}; };
export const getClient = (clientConfig: ClientConfig) => { export function getClient(clientConfig: ClientConfig) {
return flow([ return flow([
Object.keys, Object.keys,
map((key: string) => [key, configureFn(clientConfig, clientFns[key])]), map((key: string) => [key, configureFn(clientConfig, clientFns[key])]),
@ -170,4 +175,4 @@ export const getClient = (clientConfig: ClientConfig) => {
enabled: clientConfig.enabled, enabled: clientConfig.enabled,
}), }),
])(clientFns); ])(clientFns);
}; }

View File

@ -129,12 +129,15 @@ type MediaFile = {
path: string; path: string;
}; };
const withCmsLabel = (pr: GitHubPull, cmsLabelPrefix: string) => function withCmsLabel(pr: GitHubPull, cmsLabelPrefix: string) {
pr.labels.some(l => isCMSLabel(l.name, cmsLabelPrefix)); return pr.labels.some(l => isCMSLabel(l.name, cmsLabelPrefix));
const withoutCmsLabel = (pr: GitHubPull, cmsLabelPrefix: string) => }
pr.labels.every(l => !isCMSLabel(l.name, cmsLabelPrefix));
const getTreeFiles = (files: GitHubCompareFiles) => { function withoutCmsLabel(pr: GitHubPull, cmsLabelPrefix: string) {
return pr.labels.every(l => !isCMSLabel(l.name, cmsLabelPrefix));
}
function getTreeFiles(files: GitHubCompareFiles) {
const treeFiles = files.reduce((arr, file) => { const treeFiles = files.reduce((arr, file) => {
if (file.status === 'removed') { if (file.status === 'removed') {
// delete the file // delete the file
@ -152,7 +155,7 @@ const getTreeFiles = (files: GitHubCompareFiles) => {
}, [] as { sha: string | null; path: string }[]); }, [] as { sha: string | null; path: string }[]);
return treeFiles; return treeFiles;
}; }
export type Diff = { export type Diff = {
path: string; path: string;
@ -446,7 +449,7 @@ export default class API {
headers: { Accept: 'application/vnd.github.v3.raw' }, headers: { Accept: 'application/vnd.github.v3.raw' },
}; };
const errorHandler = (err: Error) => { function errorHandler(err: Error) {
if (err.message === 'Not Found') { if (err.message === 'Not Found') {
console.log( console.log(
'%c %s does not have metadata', '%c %s does not have metadata',
@ -455,7 +458,7 @@ export default class API {
); );
} }
throw err; throw err;
}; }
if (!this.useOpenAuthoring) { if (!this.useOpenAuthoring) {
const result = await this.request( const result = await this.request(

View File

@ -69,14 +69,14 @@ type GraphQLPullRequest = {
}; };
}; };
const transformPullRequest = (pr: GraphQLPullRequest) => { function transformPullRequest(pr: GraphQLPullRequest) {
return { return {
...pr, ...pr,
labels: pr.labels.nodes, labels: pr.labels.nodes,
head: { ref: pr.headRefName, sha: pr.headRefOid, repo: { fork: pr.repository.isFork } }, head: { ref: pr.headRefName, sha: pr.headRefOid, repo: { fork: pr.repository.isFork } },
base: { ref: pr.baseRefName, sha: pr.baseRefOid }, base: { ref: pr.baseRefName, sha: pr.baseRefOid },
}; };
}; }
type Error = GraphQLError & { type: string }; type Error = GraphQLError & { type: string };

View File

@ -8,7 +8,7 @@ describe('github API', () => {
jest.resetAllMocks(); jest.resetAllMocks();
}); });
const mockAPI = (api, responses) => { function mockAPI(api, responses) {
api.request = jest.fn().mockImplementation((path, options = {}) => { api.request = jest.fn().mockImplementation((path, options = {}) => {
const normalizedPath = path.indexOf('?') !== -1 ? path.substr(0, path.indexOf('?')) : path; const normalizedPath = path.indexOf('?') !== -1 ? path.substr(0, path.indexOf('?')) : path;
const response = responses[normalizedPath]; const response = responses[normalizedPath];
@ -16,7 +16,7 @@ describe('github API', () => {
? Promise.resolve(response(options)) ? Promise.resolve(response(options))
: Promise.reject(new Error(`No response for path '${normalizedPath}'`)); : Promise.reject(new Error(`No response for path '${normalizedPath}'`));
}); });
}; }
describe('editorialWorkflowGit', () => { describe('editorialWorkflowGit', () => {
it('should create PR with correct base branch name when publishing with editorial workflow', () => { it('should create PR with correct base branch name when publishing with editorial workflow', () => {

View File

@ -62,7 +62,7 @@ export const statues = gql`
${fragments.object} ${fragments.object}
`; `;
const buildFilesQuery = (depth = 1) => { function buildFilesQuery(depth = 1) {
const PLACE_HOLDER = 'PLACE_HOLDER'; const PLACE_HOLDER = 'PLACE_HOLDER';
let query = oneLine` let query = oneLine`
...ObjectParts ...ObjectParts
@ -93,9 +93,10 @@ const buildFilesQuery = (depth = 1) => {
query = query.replace(PLACE_HOLDER, ''); query = query.replace(PLACE_HOLDER, '');
return query; return query;
}; }
export const files = (depth: number) => gql` export function files(depth: number) {
return gql`
query files($owner: String!, $name: String!, $expression: String!) { query files($owner: String!, $name: String!, $expression: String!) {
repository(owner: $owner, name: $name) { repository(owner: $owner, name: $name) {
...RepositoryParts ...RepositoryParts
@ -107,7 +108,8 @@ export const files = (depth: number) => gql`
${fragments.repository} ${fragments.repository}
${fragments.object} ${fragments.object}
${fragments.fileEntry} ${fragments.fileEntry}
`; `;
}
const branchQueryPart = ` const branchQueryPart = `
branch: ref(qualifiedName: $qualifiedName) { branch: ref(qualifiedName: $qualifiedName) {

View File

@ -171,14 +171,14 @@ type GitLabCommit = {
message: string; message: string;
}; };
export const getMaxAccess = (groups: { group_access_level: number }[]) => { export function getMaxAccess(groups: { group_access_level: number }[]) {
return groups.reduce((previous, current) => { return groups.reduce((previous, current) => {
if (current.group_access_level > previous.group_access_level) { if (current.group_access_level > previous.group_access_level) {
return current; return current;
} }
return previous; return previous;
}, groups[0]); }, groups[0]);
}; }
export default class API { export default class API {
apiRoot: string; apiRoot: string;

View File

@ -8,7 +8,7 @@ import AuthenticationPage from '../AuthenticationPage';
const { Backend, LocalStorageAuthStore } = jest.requireActual('netlify-cms-core/src/backend'); const { Backend, LocalStorageAuthStore } = jest.requireActual('netlify-cms-core/src/backend');
const generateEntries = (path, length) => { function generateEntries(path, length) {
const entries = Array.from({ length }, (val, idx) => { const entries = Array.from({ length }, (val, idx) => {
const count = idx + 1; const count = idx + 1;
const id = `00${count}`.slice(-3); const id = `00${count}`.slice(-3);
@ -37,7 +37,7 @@ const generateEntries = (path, length) => {
{}, {},
), ),
}; };
}; }
const manyEntries = generateEntries('many-entries', 500); const manyEntries = generateEntries('many-entries', 500);
@ -222,8 +222,10 @@ describe('gitlab backend', () => {
const pageNum = parseInt(page, 10); const pageNum = parseInt(page, 10);
const pageCountNum = parseInt(pageCount, 10); const pageCountNum = parseInt(pageCount, 10);
const url = `${backend.implementation.apiRoot}${basePath}`; const url = `${backend.implementation.apiRoot}${basePath}`;
const link = linkPage =>
`<${url}?id=${expectedRepo}&page=${linkPage}&path=${path}&per_page=${perPage}&recursive=false>`; function link(linkPage) {
return `<${url}?id=${expectedRepo}&page=${linkPage}&path=${path}&per_page=${perPage}&recursive=false>`;
}
const linkHeader = oneLine` const linkHeader = oneLine`
${link(1)}; rel="first", ${link(1)}; rel="first",

View File

@ -14,11 +14,10 @@ import {
} from 'netlify-cms-lib-util'; } from 'netlify-cms-lib-util';
import AuthenticationPage from './AuthenticationPage'; import AuthenticationPage from './AuthenticationPage';
const serializeAsset = async (assetProxy: AssetProxy) => { async function serializeAsset(assetProxy: AssetProxy) {
const base64content = await assetProxy.toBase64!(); const base64content = await assetProxy.toBase64!();
return { path: assetProxy.path, content: base64content, encoding: 'base64' }; return { path: assetProxy.path, content: base64content, encoding: 'base64' };
}; }
type MediaFile = { type MediaFile = {
id: string; id: string;
@ -28,7 +27,7 @@ type MediaFile = {
path: string; path: string;
}; };
const deserializeMediaFile = ({ id, content, encoding, path, name }: MediaFile) => { function deserializeMediaFile({ id, content, encoding, path, name }: MediaFile) {
let byteArray = new Uint8Array(0); let byteArray = new Uint8Array(0);
if (encoding !== 'base64') { if (encoding !== 'base64') {
console.error(`Unsupported encoding '${encoding}' for file '${path}'`); console.error(`Unsupported encoding '${encoding}' for file '${path}'`);
@ -43,7 +42,7 @@ const deserializeMediaFile = ({ id, content, encoding, path, name }: MediaFile)
const file = blobToFileObj(name, blob); const file = blobToFileObj(name, blob);
const url = URL.createObjectURL(file); const url = URL.createObjectURL(file);
return { id, name, path, file, size: file.size, url, displayURL: url }; return { id, name, path, file, size: file.size, url, displayURL: url };
}; }
export default class ProxyBackend implements Implementation { export default class ProxyBackend implements Implementation {
proxyUrl: string; proxyUrl: string;

View File

@ -74,13 +74,13 @@ function deleteFile(path: string, tree: RepoTree) {
const pageSize = 10; const pageSize = 10;
const getCursor = ( function getCursor(
folder: string, folder: string,
extension: string, extension: string,
entries: ImplementationEntry[], entries: ImplementationEntry[],
index: number, index: number,
depth: number, depth: number,
) => { ) {
const count = entries.length; const count = entries.length;
const pageCount = Math.floor(count / pageSize); const pageCount = Math.floor(count / pageSize);
return Cursor.create({ return Cursor.create({
@ -91,16 +91,16 @@ const getCursor = (
meta: { index, count, pageSize, pageCount }, meta: { index, count, pageSize, pageCount },
data: { folder, extension, index, pageCount, depth }, data: { folder, extension, index, pageCount, depth },
}); });
}; }
export const getFolderFiles = ( export function getFolderFiles(
tree: RepoTree, tree: RepoTree,
folder: string, folder: string,
extension: string, extension: string,
depth: number, depth: number,
files = [] as RepoFile[], files = [] as RepoFile[],
path = folder, path = folder,
) => { ) {
if (depth <= 0) { if (depth <= 0) {
return files; return files;
} }
@ -118,7 +118,7 @@ export const getFolderFiles = (
}); });
return files; return files;
}; }
export default class TestBackend implements Implementation { export default class TestBackend implements Implementation {
mediaFolder: string; mediaFolder: string;

View File

@ -817,14 +817,14 @@ describe('config', () => {
}); });
describe('detectProxyServer', () => { describe('detectProxyServer', () => {
const assetFetchCalled = (url = 'http://localhost:8081/api/v1') => { function assetFetchCalled(url = 'http://localhost:8081/api/v1') {
expect(global.fetch).toHaveBeenCalledTimes(1); expect(global.fetch).toHaveBeenCalledTimes(1);
expect(global.fetch).toHaveBeenCalledWith(url, { expect(global.fetch).toHaveBeenCalledWith(url, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'info' }), body: JSON.stringify({ action: 'info' }),
}); });
}; }
beforeEach(() => { beforeEach(() => {
delete window.location; delete window.location;

View File

@ -13,7 +13,7 @@ export const CONFIG_REQUEST = 'CONFIG_REQUEST';
export const CONFIG_SUCCESS = 'CONFIG_SUCCESS'; export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
export const CONFIG_FAILURE = 'CONFIG_FAILURE'; export const CONFIG_FAILURE = 'CONFIG_FAILURE';
const traverseFieldsJS = (fields, updater) => { function traverseFieldsJS(fields, updater) {
return fields.map(field => { return fields.map(field => {
let newField = updater(field); let newField = updater(field);
if (newField.fields) { if (newField.fields) {
@ -26,9 +26,9 @@ const traverseFieldsJS = (fields, updater) => {
return newField; return newField;
}); });
}; }
const getConfigUrl = () => { function getConfigUrl() {
const validTypes = { 'text/yaml': 'yaml', 'application/x-yaml': 'yaml' }; const validTypes = { 'text/yaml': 'yaml', 'application/x-yaml': 'yaml' };
const configLinkEl = document.querySelector('link[rel="cms-config-url"]'); const configLinkEl = document.querySelector('link[rel="cms-config-url"]');
const isValidLink = configLinkEl && validTypes[configLinkEl.type] && get(configLinkEl, 'href'); const isValidLink = configLinkEl && validTypes[configLinkEl.type] && get(configLinkEl, 'href');
@ -38,14 +38,14 @@ const getConfigUrl = () => {
return link; return link;
} }
return 'config.yml'; return 'config.yml';
}; }
const setDefaultPublicFolder = map => { function setDefaultPublicFolder(map) {
if (map.has('media_folder') && !map.has('public_folder')) { if (map.has('media_folder') && !map.has('public_folder')) {
map = map.set('public_folder', map.get('media_folder')); map = map.set('public_folder', map.get('media_folder'));
} }
return map; return map;
}; }
// Mapping between existing camelCase and its snake_case counterpart // Mapping between existing camelCase and its snake_case counterpart
const WIDGET_KEY_MAP = { const WIDGET_KEY_MAP = {
@ -60,7 +60,7 @@ const WIDGET_KEY_MAP = {
optionsLength: 'options_length', optionsLength: 'options_length',
}; };
const setSnakeCaseConfig = field => { function setSnakeCaseConfig(field) {
const deprecatedKeys = Object.keys(WIDGET_KEY_MAP).filter(camel => camel in field); const deprecatedKeys = Object.keys(WIDGET_KEY_MAP).filter(camel => camel in field);
const snakeValues = deprecatedKeys.map(camel => { const snakeValues = deprecatedKeys.map(camel => {
const snake = WIDGET_KEY_MAP[camel]; const snake = WIDGET_KEY_MAP[camel];
@ -71,18 +71,18 @@ const setSnakeCaseConfig = field => {
}); });
return Object.assign({}, field, ...snakeValues); return Object.assign({}, field, ...snakeValues);
}; }
const setI18nField = field => { function setI18nField(field) {
if (field.get(I18N) === true) { if (field.get(I18N) === true) {
field = field.set(I18N, I18N_FIELD.TRANSLATE); field = field.set(I18N, I18N_FIELD.TRANSLATE);
} else if (field.get(I18N) === false || !field.has(I18N)) { } else if (field.get(I18N) === false || !field.has(I18N)) {
field = field.set(I18N, I18N_FIELD.NONE); field = field.set(I18N, I18N_FIELD.NONE);
} }
return field; return field;
}; }
const setI18nDefaults = (defaultI18n, collectionOrFile) => { function setI18nDefaults(defaultI18n, collectionOrFile) {
if (defaultI18n && collectionOrFile.has(I18N)) { if (defaultI18n && collectionOrFile.has(I18N)) {
const collectionOrFileI18n = collectionOrFile.get(I18N); const collectionOrFileI18n = collectionOrFile.get(I18N);
if (collectionOrFileI18n === true) { if (collectionOrFileI18n === true) {
@ -121,17 +121,17 @@ const setI18nDefaults = (defaultI18n, collectionOrFile) => {
} }
} }
return collectionOrFile; return collectionOrFile;
}; }
const throwOnInvalidFileCollectionStructure = i18n => { function throwOnInvalidFileCollectionStructure(i18n) {
if (i18n && i18n.get('structure') !== I18N_STRUCTURE.SINGLE_FILE) { if (i18n && i18n.get('structure') !== I18N_STRUCTURE.SINGLE_FILE) {
throw new Error( throw new Error(
`i18n configuration for files collections is limited to ${I18N_STRUCTURE.SINGLE_FILE} structure`, `i18n configuration for files collections is limited to ${I18N_STRUCTURE.SINGLE_FILE} structure`,
); );
} }
}; }
const throwOnMissingDefaultLocale = i18n => { function throwOnMissingDefaultLocale(i18n) {
if (i18n && !i18n.get('locales').includes(i18n.get('default_locale'))) { if (i18n && !i18n.get('locales').includes(i18n.get('default_locale'))) {
throw new Error( throw new Error(
`i18n locales '${i18n.get('locales').join(', ')}' are missing the default locale ${i18n.get( `i18n locales '${i18n.get('locales').join(', ')}' are missing the default locale ${i18n.get(
@ -139,9 +139,9 @@ const throwOnMissingDefaultLocale = i18n => {
)}`, )}`,
); );
} }
}; }
const setViewPatternsDefaults = (key, collection) => { function setViewPatternsDefaults(key, collection) {
if (!collection.has(key)) { if (!collection.has(key)) {
collection = collection.set(key, fromJS([])); collection = collection.set(key, fromJS([]));
} else { } else {
@ -152,17 +152,17 @@ const setViewPatternsDefaults = (key, collection) => {
} }
return collection; return collection;
}; }
const defaults = { const defaults = {
publish_mode: SIMPLE_PUBLISH_MODE, publish_mode: SIMPLE_PUBLISH_MODE,
}; };
const hasIntegration = (config, collection) => { function hasIntegration(config, collection) {
const integrations = getIntegrations(config); const integrations = getIntegrations(config);
const integration = selectIntegration(integrations, collection.get('name'), 'listEntries'); const integration = selectIntegration(integrations, collection.get('name'), 'listEntries');
return !!integration; return !!integration;
}; }
export function normalizeConfig(config) { export function normalizeConfig(config) {
const { collections = [] } = config; const { collections = [] } = config;
@ -390,7 +390,7 @@ export async function detectProxyServer(localBackend) {
return {}; return {};
} }
const getPublishMode = (config, publishModes, backendType) => { function getPublishMode(config, publishModes, backendType) {
if (config.publish_mode && publishModes && !publishModes.includes(config.publish_mode)) { if (config.publish_mode && publishModes && !publishModes.includes(config.publish_mode)) {
const newPublishMode = publishModes[0]; const newPublishMode = publishModes[0];
console.log( console.log(
@ -400,9 +400,9 @@ const getPublishMode = (config, publishModes, backendType) => {
} }
return config.publish_mode; return config.publish_mode;
}; }
export const handleLocalBackend = async config => { export async function handleLocalBackend(config) {
if (!config.local_backend) { if (!config.local_backend) {
return config; return config;
} }
@ -421,7 +421,7 @@ export const handleLocalBackend = async config => {
...(publishMode && { publish_mode: publishMode }), ...(publishMode && { publish_mode: publishMode }),
backend: { ...config.backend, name: 'proxy', proxy_url: proxyUrl }, backend: { ...config.backend, name: 'proxy', proxy_url: proxyUrl },
}; };
}; }
export function loadConfig(manualConfig = {}, onLoad) { export function loadConfig(manualConfig = {}, onLoad) {
if (window.CMS_CONFIG) { if (window.CMS_CONFIG) {

View File

@ -154,7 +154,7 @@ export function entriesFailed(collection: Collection, error: Error) {
}; };
} }
const getAllEntries = async (state: State, collection: Collection) => { async function getAllEntries(state: State, collection: Collection) {
const backend = currentBackend(state.config); const backend = currentBackend(state.config);
const integration = selectIntegration(state, collection.get('name'), 'listEntries'); const integration = selectIntegration(state, collection.get('name'), 'listEntries');
const provider: Backend = integration const provider: Backend = integration
@ -162,7 +162,7 @@ const getAllEntries = async (state: State, collection: Collection) => {
: backend; : backend;
const entries = await provider.listAllEntries(collection); const entries = await provider.listAllEntries(collection);
return entries; return entries;
}; }
export function sortByField( export function sortByField(
collection: Collection, collection: Collection,
@ -555,7 +555,7 @@ const appendActions = fromJS({
['append_next']: { action: 'next', append: true }, ['append_next']: { action: 'next', append: true },
}); });
const addAppendActionsToCursor = (cursor: Cursor) => { function addAppendActionsToCursor(cursor: Cursor) {
return Cursor.create(cursor).updateStore('actions', (actions: Set<string>) => { return Cursor.create(cursor).updateStore('actions', (actions: Set<string>) => {
return actions.union( return actions.union(
appendActions appendActions
@ -563,7 +563,7 @@ const addAppendActionsToCursor = (cursor: Cursor) => {
.keySeq(), .keySeq(),
); );
}); });
}; }
export function loadEntries(collection: Collection, page = 0) { export function loadEntries(collection: Collection, page = 0) {
return async (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => { return async (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
@ -692,16 +692,16 @@ export function traverseCollectionCursor(collection: Collection, action: string)
}; };
} }
const escapeHtml = (unsafe: string) => { function escapeHtml(unsafe: string) {
return unsafe return unsafe
.replace(/&/g, '&amp;') .replace(/&/g, '&amp;')
.replace(/</g, '&lt;') .replace(/</g, '&lt;')
.replace(/>/g, '&gt;') .replace(/>/g, '&gt;')
.replace(/"/g, '&quot;') .replace(/"/g, '&quot;')
.replace(/'/g, '&#039;'); .replace(/'/g, '&#039;');
}; }
const processValue = (unsafe: string) => { function processValue(unsafe: string) {
if (['true', 'True', 'TRUE'].includes(unsafe)) { if (['true', 'True', 'TRUE'].includes(unsafe)) {
return true; return true;
} }
@ -710,10 +710,15 @@ const processValue = (unsafe: string) => {
} }
return escapeHtml(unsafe); return escapeHtml(unsafe);
}; }
const getDataFields = (fields: EntryFields) => fields.filter(f => !f!.get('meta')).toList(); function getDataFields(fields: EntryFields) {
const getMetaFields = (fields: EntryFields) => fields.filter(f => f!.get('meta') === true).toList(); return fields.filter(f => !f!.get('meta')).toList();
}
function getMetaFields(fields: EntryFields) {
return fields.filter(f => f!.get('meta') === true).toList();
}
export function createEmptyDraft(collection: Collection, search: string) { export function createEmptyDraft(collection: Collection, search: string) {
return async (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => { return async (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
@ -785,7 +790,10 @@ export function createEmptyDraftData(
const list = item.get('widget') == 'list'; const list = item.get('widget') == 'list';
const name = item.get('name'); const name = item.get('name');
const defaultValue = item.get('default', null); const defaultValue = item.get('default', null);
const isEmptyDefaultValue = (val: unknown) => [[{}], {}].some(e => isEqual(val, e));
function isEmptyDefaultValue(val: unknown) {
return [[{}], {}].some(e => isEqual(val, e));
}
if (List.isList(subfields)) { if (List.isList(subfields)) {
const subDefaultValue = list const subDefaultValue = list
@ -831,9 +839,9 @@ function createEmptyDraftI18nData(collection: Collection, dataFields: EntryField
return {}; return {};
} }
const skipField = (field: EntryField) => { function skipField(field: EntryField) {
return field.get(I18N) !== I18N_FIELD.DUPLICATE && field.get(I18N) !== I18N_FIELD.TRANSLATE; return field.get(I18N) !== I18N_FIELD.DUPLICATE && field.get(I18N) !== I18N_FIELD.TRANSLATE;
}; }
const i18nData = createEmptyDraftData(dataFields, true, skipField); const i18nData = createEmptyDraftData(dataFields, true, skipField);
return duplicateDefaultI18nFields(collection, i18nData); return duplicateDefaultI18nFields(collection, i18nData);
@ -850,23 +858,25 @@ export function getMediaAssets({ entry }: { entry: EntryMap }) {
return assets; return assets;
} }
export const getSerializedEntry = (collection: Collection, entry: Entry) => { export function getSerializedEntry(collection: Collection, entry: Entry) {
/** /**
* Serialize the values of any fields with registered serializers, and * Serialize the values of any fields with registered serializers, and
* update the entry and entryDraft with the serialized values. * update the entry and entryDraft with the serialized values.
*/ */
const fields = selectFields(collection, entry.get('slug')); const fields = selectFields(collection, entry.get('slug'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const serializeData = (data: any) => { function serializeData(data: any) {
return serializeValues(data, fields); return serializeValues(data, fields);
}; }
const serializedData = serializeData(entry.get('data')); const serializedData = serializeData(entry.get('data'));
let serializedEntry = entry.set('data', serializedData); let serializedEntry = entry.set('data', serializedData);
if (hasI18n(collection)) { if (hasI18n(collection)) {
serializedEntry = serializeI18n(collection, serializedEntry, serializeData); serializedEntry = serializeI18n(collection, serializedEntry, serializeData);
} }
return serializedEntry; return serializedEntry;
}; }
export function persistEntry(collection: Collection) { export function persistEntry(collection: Collection) {
return async (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => { return async (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
@ -982,11 +992,11 @@ export function deleteEntry(collection: Collection, slug: string) {
}; };
} }
const getPathError = ( function getPathError(
path: string | undefined, path: string | undefined,
key: string, key: string,
t: (key: string, args: Record<string, unknown>) => string, t: (key: string, args: Record<string, unknown>) => string,
) => { ) {
return { return {
error: { error: {
type: ValidationErrorTypes.CUSTOM, type: ValidationErrorTypes.CUSTOM,
@ -995,7 +1005,7 @@ const getPathError = (
}), }),
}, },
}; };
}; }
export function validateMetaField( export function validateMetaField(
state: State, state: State,

View File

@ -82,10 +82,10 @@ export function boundGetAsset(
collection: Collection, collection: Collection,
entry: EntryMap, entry: EntryMap,
) { ) {
const bound = (path: string, field: EntryField) => { function bound(path: string, field: EntryField) {
const asset = dispatch(getAsset({ collection, entry, path, field })); const asset = dispatch(getAsset({ collection, entry, path, field }));
return asset; return asset;
}; }
return bound; return bound;
} }

View File

@ -158,8 +158,8 @@ export function loadMedia(
} }
dispatch(mediaLoading(page)); dispatch(mediaLoading(page));
const loadFunction = () => function loadFunction() {
backend return backend
.getMedia() .getMedia()
.then(files => dispatch(mediaLoaded(files))) .then(files => dispatch(mediaLoaded(files)))
.catch((error: { status?: number }) => { .catch((error: { status?: number }) => {
@ -171,6 +171,7 @@ export function loadMedia(
dispatch(mediaLoadFailed()); dispatch(mediaLoadFailed());
} }
}); });
}
if (delay > 0) { if (delay > 0) {
return new Promise(resolve => { return new Promise(resolve => {

View File

@ -3,19 +3,19 @@ import { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux'; import { AnyAction } from 'redux';
import { State } from '../types/redux'; import { State } from '../types/redux';
export const waitUntil = ({ predicate, run }: WaitActionArgs) => { export function waitUntil({ predicate, run }: WaitActionArgs) {
return { return {
type: WAIT_UNTIL_ACTION, type: WAIT_UNTIL_ACTION,
predicate, predicate,
run, run,
}; };
}; }
export const waitUntilWithTimeout = async <T>( export async function waitUntilWithTimeout<T>(
dispatch: ThunkDispatch<State, {}, AnyAction>, dispatch: ThunkDispatch<State, {}, AnyAction>,
waitActionArgs: (resolve: (value?: T) => void) => WaitActionArgs, waitActionArgs: (resolve: (value?: T) => void) => WaitActionArgs,
timeout = 30000, timeout = 30000,
): Promise<T | null> => { ): Promise<T | null> {
let waitDone = false; let waitDone = false;
const waitPromise = new Promise<T>(resolve => { const waitPromise = new Promise<T>(resolve => {
@ -44,4 +44,4 @@ export const waitUntilWithTimeout = async <T>(
]); ]);
return result; return result;
}; }

View File

@ -71,13 +71,13 @@ import {
const { extractTemplateVars, dateParsers, expandPath } = stringTemplate; const { extractTemplateVars, dateParsers, expandPath } = stringTemplate;
const updateAssetProxies = ( function updateAssetProxies(
assetProxies: AssetProxy[], assetProxies: AssetProxy[],
config: Config, config: Config,
collection: Collection, collection: Collection,
entryDraft: EntryDraft, entryDraft: EntryDraft,
path: string, path: string,
) => { ) {
assetProxies.map(asset => { assetProxies.map(asset => {
// update media files path based on entry path // update media files path based on entry path
const oldPath = asset.path; const oldPath = asset.path;
@ -90,7 +90,7 @@ const updateAssetProxies = (
); );
asset.path = newPath; asset.path = newPath;
}); });
}; }
export class LocalStorageAuthStore { export class LocalStorageAuthStore {
storageKey = 'netlify-cms-user'; storageKey = 'netlify-cms-user';
@ -118,7 +118,7 @@ function getEntryBackupKey(collectionName?: string, slug?: string) {
return `${baseKey}.${collectionName}${suffix}`; return `${baseKey}.${collectionName}${suffix}`;
} }
const getEntryField = (field: string, entry: EntryValue) => { function getEntryField(field: string, entry: EntryValue) {
const value = get(entry.data, field); const value = get(entry.data, field);
if (value) { if (value) {
return String(value); return String(value);
@ -131,9 +131,10 @@ const getEntryField = (field: string, entry: EntryValue) => {
return ''; return '';
} }
} }
}; }
export const extractSearchFields = (searchFields: string[]) => (entry: EntryValue) => export function extractSearchFields(searchFields: string[]) {
return (entry: EntryValue) =>
searchFields.reduce((acc, field) => { searchFields.reduce((acc, field) => {
const value = getEntryField(field, entry); const value = getEntryField(field, entry);
if (value) { if (value) {
@ -142,8 +143,9 @@ export const extractSearchFields = (searchFields: string[]) => (entry: EntryValu
return acc; return acc;
} }
}, ''); }, '');
}
export const expandSearchEntries = (entries: EntryValue[], searchFields: string[]) => { export function expandSearchEntries(entries: EntryValue[], searchFields: string[]) {
// expand the entries for the purpose of the search // expand the entries for the purpose of the search
const expandedEntries = entries.reduce((acc, e) => { const expandedEntries = entries.reduce((acc, e) => {
const expandedFields = searchFields.reduce((acc, f) => { const expandedFields = searchFields.reduce((acc, f) => {
@ -160,9 +162,9 @@ export const expandSearchEntries = (entries: EntryValue[], searchFields: string[
}, [] as (EntryValue & { field: string })[]); }, [] as (EntryValue & { field: string })[]);
return expandedEntries; return expandedEntries;
}; }
export const mergeExpandedEntries = (entries: (EntryValue & { field: string })[]) => { export function mergeExpandedEntries(entries: (EntryValue & { field: string })[]) {
// merge the search results by slug and only keep data that matched the search // merge the search results by slug and only keep data that matched the search
const fields = entries.map(f => f.field); const fields = entries.map(f => f.field);
const arrayPaths: Record<string, Set<string>> = {}; const arrayPaths: Record<string, Set<string>> = {};
@ -214,20 +216,20 @@ export const mergeExpandedEntries = (entries: (EntryValue & { field: string })[]
}); });
return Object.values(merged); return Object.values(merged);
}; }
const sortByScore = (a: fuzzy.FilterResult<EntryValue>, b: fuzzy.FilterResult<EntryValue>) => { function sortByScore(a: fuzzy.FilterResult<EntryValue>, b: fuzzy.FilterResult<EntryValue>) {
if (a.score > b.score) return -1; if (a.score > b.score) return -1;
if (a.score < b.score) return 1; if (a.score < b.score) return 1;
return 0; return 0;
}; }
export const slugFromCustomPath = (collection: Collection, customPath: string) => { export function slugFromCustomPath(collection: Collection, customPath: string) {
const folderPath = collection.get('folder', '') as string; const folderPath = collection.get('folder', '') as string;
const entryPath = customPath.toLowerCase().replace(folderPath.toLowerCase(), ''); const entryPath = customPath.toLowerCase().replace(folderPath.toLowerCase(), '');
const slug = join(dirname(trim(entryPath, '/')), basename(entryPath, extname(customPath))); const slug = join(dirname(trim(entryPath, '/')), basename(entryPath, extname(customPath)));
return slug; return slug;
}; }
interface AuthStore { interface AuthStore {
retrieve: () => User; retrieve: () => User;
@ -280,15 +282,15 @@ type Implementation = BackendImplementation & {
init: (config: ImplementationConfig, options: ImplementationInitOptions) => Implementation; init: (config: ImplementationConfig, options: ImplementationInitOptions) => Implementation;
}; };
const prepareMetaPath = (path: string, collection: Collection) => { function prepareMetaPath(path: string, collection: Collection) {
if (!selectHasMetaPath(collection)) { if (!selectHasMetaPath(collection)) {
return path; return path;
} }
const dir = dirname(path); const dir = dirname(path);
return dir.substr(collection.get('folder')!.length + 1) || '/'; return dir.substr(collection.get('folder')!.length + 1) || '/';
}; }
const collectionDepth = (collection: Collection) => { function collectionDepth(collection: Collection) {
let depth; let depth;
depth = depth =
collection.get('nested')?.get('depth') || getPathDepth(collection.get('path', '') as string); collection.get('nested')?.get('depth') || getPathDepth(collection.get('path', '') as string);
@ -298,7 +300,7 @@ const collectionDepth = (collection: Collection) => {
} }
return depth; return depth;
}; }
export class Backend { export class Backend {
implementation: Implementation; implementation: Implementation;

View File

@ -19,7 +19,7 @@ import 'what-input';
const ROOT_ID = 'nc-root'; const ROOT_ID = 'nc-root';
const TranslatedApp = ({ locale, config }) => { function TranslatedApp({ locale, config }) {
return ( return (
<I18n locale={locale} messages={getPhrases(locale)}> <I18n locale={locale} messages={getPhrases(locale)}>
<ErrorBoundary showBackup config={config}> <ErrorBoundary showBackup config={config}>
@ -29,11 +29,11 @@ const TranslatedApp = ({ locale, config }) => {
</ErrorBoundary> </ErrorBoundary>
</I18n> </I18n>
); );
}; }
const mapDispatchToProps = state => { function mapDispatchToProps(state) {
return { locale: selectLocale(state.config), config: state.config }; return { locale: selectLocale(state.config), config: state.config };
}; }
const ConnectedTranslatedApp = connect(mapDispatchToProps)(TranslatedApp); const ConnectedTranslatedApp = connect(mapDispatchToProps)(TranslatedApp);
@ -82,7 +82,8 @@ function bootstrap(opts = {}) {
/** /**
* Create connected root component. * Create connected root component.
*/ */
const Root = () => ( function Root() {
return (
<> <>
<GlobalStyles /> <GlobalStyles />
<Provider store={store}> <Provider store={store}>
@ -90,6 +91,7 @@ function bootstrap(opts = {}) {
</Provider> </Provider>
</> </>
); );
}
/** /**
* Render application root. * Render application root.

View File

@ -48,16 +48,16 @@ const ErrorCodeBlock = styled.pre`
line-height: 1.5; line-height: 1.5;
`; `;
const getDefaultPath = collections => { function getDefaultPath(collections) {
const first = collections.filter(collection => collection.get('hide') !== true).first(); const first = collections.filter(collection => collection.get('hide') !== true).first();
if (first) { if (first) {
return `/collections/${first.get('name')}`; return `/collections/${first.get('name')}`;
} else { } else {
throw new Error('Could not find a non hidden collection'); throw new Error('Could not find a non hidden collection');
} }
}; }
const RouteInCollection = ({ collections, render, ...props }) => { function RouteInCollection({ collections, render, ...props }) {
const defaultPath = getDefaultPath(collections); const defaultPath = getDefaultPath(collections);
return ( return (
<Route <Route
@ -68,7 +68,7 @@ const RouteInCollection = ({ collections, render, ...props }) => {
}} }}
/> />
); );
}; }
class App extends React.Component { class App extends React.Component {
static propTypes = { static propTypes = {

View File

@ -26,7 +26,8 @@ const styles = {
`, `,
}; };
const AppHeader = props => ( function AppHeader(props) {
return (
<header <header
css={css` css={css`
${shadows.dropMain}; ${shadows.dropMain};
@ -39,7 +40,8 @@ const AppHeader = props => (
`} `}
{...props} {...props}
/> />
); );
}
const AppHeaderContent = styled.div` const AppHeaderContent = styled.div`
display: flex; display: flex;

View File

@ -8,11 +8,13 @@ const NotFoundContainer = styled.div`
margin: ${lengths.pageMargin}; margin: ${lengths.pageMargin};
`; `;
const NotFoundPage = ({ t }) => ( function NotFoundPage({ t }) {
return (
<NotFoundContainer> <NotFoundContainer>
<h2>{t('app.notFoundPage.header')}</h2> <h2>{t('app.notFoundPage.header')}</h2>
</NotFoundContainer> </NotFoundContainer>
); );
}
NotFoundPage.propTypes = { NotFoundPage.propTypes = {
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,

View File

@ -183,7 +183,7 @@ const mapDispatchToProps = {
groupByField, groupByField,
}; };
const mergeProps = (stateProps, dispatchProps, ownProps) => { function mergeProps(stateProps, dispatchProps, ownProps) {
return { return {
...stateProps, ...stateProps,
...ownProps, ...ownProps,
@ -193,7 +193,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
onGroupClick: group => dispatchProps.groupByField(stateProps.collection, group), onGroupClick: group => dispatchProps.groupByField(stateProps.collection, group),
onChangeViewStyle: viewStyle => dispatchProps.changeViewStyle(viewStyle), onChangeViewStyle: viewStyle => dispatchProps.changeViewStyle(viewStyle),
}; };
}; }
const ConnectedCollection = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Collection); const ConnectedCollection = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Collection);

View File

@ -19,7 +19,7 @@ const CollectionControlsContainer = styled.div`
} }
`; `;
const CollectionControls = ({ function CollectionControls({
viewStyle, viewStyle,
onChangeViewStyle, onChangeViewStyle,
sortableFields, sortableFields,
@ -32,7 +32,7 @@ const CollectionControls = ({
t, t,
filter, filter,
group, group,
}) => { }) {
return ( return (
<CollectionControlsContainer> <CollectionControlsContainer>
<ViewStyleControl viewStyle={viewStyle} onChangeViewStyle={onChangeViewStyle} /> <ViewStyleControl viewStyle={viewStyle} onChangeViewStyle={onChangeViewStyle} />
@ -52,6 +52,6 @@ const CollectionControls = ({
)} )}
</CollectionControlsContainer> </CollectionControlsContainer>
); );
}; }
export default CollectionControls; export default CollectionControls;

View File

@ -35,7 +35,7 @@ const CollectionTopDescription = styled.p`
margin-bottom: 0; margin-bottom: 0;
`; `;
const getCollectionProps = collection => { function getCollectionProps(collection) {
const collectionLabel = collection.get('label'); const collectionLabel = collection.get('label');
const collectionLabelSingular = collection.get('label_singular'); const collectionLabelSingular = collection.get('label_singular');
const collectionDescription = collection.get('description'); const collectionDescription = collection.get('description');
@ -45,9 +45,9 @@ const getCollectionProps = collection => {
collectionLabelSingular, collectionLabelSingular,
collectionDescription, collectionDescription,
}; };
}; }
const CollectionTop = ({ collection, newEntryUrl, t }) => { function CollectionTop({ collection, newEntryUrl, t }) {
const { collectionLabel, collectionLabelSingular, collectionDescription } = getCollectionProps( const { collectionLabel, collectionLabelSingular, collectionDescription } = getCollectionProps(
collection, collection,
t, t,
@ -70,7 +70,7 @@ const CollectionTop = ({ collection, newEntryUrl, t }) => {
) : null} ) : null}
</CollectionTopContainer> </CollectionTopContainer>
); );
}; }
CollectionTop.propTypes = { CollectionTop.propTypes = {
collection: ImmutablePropTypes.map.isRequired, collection: ImmutablePropTypes.map.isRequired,

View File

@ -14,7 +14,7 @@ const Button = styled(StyledDropdownButton)`
} }
`; `;
export const ControlButton = ({ active, title }) => { export function ControlButton({ active, title }) {
return ( return (
<Button <Button
css={css` css={css`
@ -24,4 +24,4 @@ export const ControlButton = ({ active, title }) => {
{title} {title}
</Button> </Button>
); );
}; }

View File

@ -16,7 +16,7 @@ const NoEntriesMessage = styled(PaginationMessage)`
margin-top: 16px; margin-top: 16px;
`; `;
const Entries = ({ function Entries({
collections, collections,
entries, entries,
isFetching, isFetching,
@ -25,7 +25,7 @@ const Entries = ({
handleCursorActions, handleCursorActions,
t, t,
page, page,
}) => { }) {
const loadingMessages = [ const loadingMessages = [
t('collection.entries.loadingEntries'), t('collection.entries.loadingEntries'),
t('collection.entries.cachingEntries'), t('collection.entries.cachingEntries'),
@ -56,7 +56,7 @@ const Entries = ({
} }
return <NoEntriesMessage>{t('collection.entries.noEntries')}</NoEntriesMessage>; return <NoEntriesMessage>{t('collection.entries.noEntries')}</NoEntriesMessage>;
}; }
Entries.propTypes = { Entries.propTypes = {
collections: ImmutablePropTypes.iterable.isRequired, collections: ImmutablePropTypes.iterable.isRequired,

View File

@ -28,11 +28,11 @@ const GroupHeading = styled.h2`
const GroupContainer = styled.div``; const GroupContainer = styled.div``;
const getGroupEntries = (entries, paths) => { function getGroupEntries(entries, paths) {
return entries.filter(entry => paths.has(entry.get('path'))); return entries.filter(entry => paths.has(entry.get('path')));
}; }
const getGroupTitle = (group, t) => { function getGroupTitle(group, t) {
const { label, value } = group; const { label, value } = group;
if (value === undefined) { if (value === undefined) {
return t('collection.groups.other'); return t('collection.groups.other');
@ -41,9 +41,9 @@ const getGroupTitle = (group, t) => {
return value ? label : t('collection.groups.negateLabel', { label }); return value ? label : t('collection.groups.negateLabel', { label });
} }
return `${label} ${value}`.trim(); return `${label} ${value}`.trim();
}; }
const withGroups = (groups, entries, EntriesToRender, t) => { function withGroups(groups, entries, EntriesToRender, t) {
return groups.map(group => { return groups.map(group => {
const title = getGroupTitle(group, t); const title = getGroupTitle(group, t);
return ( return (
@ -53,7 +53,7 @@ const withGroups = (groups, entries, EntriesToRender, t) => {
</GroupContainer> </GroupContainer>
); );
}); });
}; }
export class EntriesCollection extends React.Component { export class EntriesCollection extends React.Component {
static propTypes = { static propTypes = {
@ -114,7 +114,7 @@ export class EntriesCollection extends React.Component {
} }
} }
export const filterNestedEntries = (path, collectionFolder, entries) => { export function filterNestedEntries(path, collectionFolder, entries) {
const filtered = entries.filter(e => { const filtered = entries.filter(e => {
const entryPath = e.get('path').substring(collectionFolder.length + 1); const entryPath = e.get('path').substring(collectionFolder.length + 1);
if (!entryPath.startsWith(path)) { if (!entryPath.startsWith(path)) {
@ -132,7 +132,7 @@ export const filterNestedEntries = (path, collectionFolder, entries) => {
} }
}); });
return filtered; return filtered;
}; }
function mapStateToProps(state, ownProps) { function mapStateToProps(state, ownProps) {
const { collection, viewStyle, filterTerm } = ownProps; const { collection, viewStyle, filterTerm } = ownProps;

View File

@ -84,7 +84,7 @@ const CardImage = styled.div`
height: 150px; height: 150px;
`; `;
const EntryCard = ({ function EntryCard({
path, path,
summary, summary,
image, image,
@ -92,7 +92,7 @@ const EntryCard = ({
collectionLabel, collectionLabel,
viewStyle = VIEW_STYLE_LIST, viewStyle = VIEW_STYLE_LIST,
getAsset, getAsset,
}) => { }) {
if (viewStyle === VIEW_STYLE_LIST) { if (viewStyle === VIEW_STYLE_LIST) {
return ( return (
<ListCard> <ListCard>
@ -117,9 +117,9 @@ const EntryCard = ({
</GridCard> </GridCard>
); );
} }
}; }
const mapStateToProps = (state, ownProps) => { function mapStateToProps(state, ownProps) {
const { entry, inferedFields, collection } = ownProps; const { entry, inferedFields, collection } = ownProps;
const entryData = entry.get('data'); const entryData = entry.get('data');
const summary = selectEntryCollectionTitle(collection, entry); const summary = selectEntryCollectionTitle(collection, entry);
@ -140,22 +140,22 @@ const mapStateToProps = (state, ownProps) => {
?.find(f => f.get('name') === inferedFields.imageField && f.get('widget') === 'image'), ?.find(f => f.get('name') === inferedFields.imageField && f.get('widget') === 'image'),
isLoadingAsset, isLoadingAsset,
}; };
}; }
const mapDispatchToProps = dispatch => { function mapDispatchToProps(dispatch) {
return { return {
boundGetAsset: (collection, entry) => boundGetAsset(dispatch, collection, entry), boundGetAsset: (collection, entry) => boundGetAsset(dispatch, collection, entry),
}; };
}; }
const mergeProps = (stateProps, dispatchProps, ownProps) => { function mergeProps(stateProps, dispatchProps, ownProps) {
return { return {
...stateProps, ...stateProps,
...dispatchProps, ...dispatchProps,
...ownProps, ...ownProps,
getAsset: dispatchProps.boundGetAsset(ownProps.collection, ownProps.entry), getAsset: dispatchProps.boundGetAsset(ownProps.collection, ownProps.entry),
}; };
}; }
const ConnectedEntryCard = connect(mapStateToProps, mapDispatchToProps, mergeProps)(EntryCard); const ConnectedEntryCard = connect(mapStateToProps, mapDispatchToProps, mergeProps)(EntryCard);

View File

@ -13,14 +13,15 @@ jest.mock('../Entries', () => 'mock-entries');
const middlewares = []; const middlewares = [];
const mockStore = configureStore(middlewares); const mockStore = configureStore(middlewares);
const renderWithRedux = (component, { store } = {}) => { function renderWithRedux(component, { store } = {}) {
function Wrapper({ children }) { function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>; return <Provider store={store}>{children}</Provider>;
} }
return render(component, { wrapper: Wrapper });
};
const toEntriesState = (collection, entriesArray) => { return render(component, { wrapper: Wrapper });
}
function toEntriesState(collection, entriesArray) {
const entries = entriesArray.reduce( const entries = entriesArray.reduce(
(acc, entry) => { (acc, entry) => {
acc.entities[`${collection.get('name')}.${entry.slug}`] = entry; acc.entities[`${collection.get('name')}.${entry.slug}`] = entry;
@ -30,7 +31,7 @@ const toEntriesState = (collection, entriesArray) => {
{ pages: { [collection.get('name')]: { ids: [] } }, entities: {} }, { pages: { [collection.get('name')]: { ids: [] } }, entities: {} },
); );
return fromJS(entries); return fromJS(entries);
}; }
describe('filterNestedEntries', () => { describe('filterNestedEntries', () => {
it('should return only immediate children for non root path', () => { it('should return only immediate children for non root path', () => {

View File

@ -3,7 +3,7 @@ import { translate } from 'react-polyglot';
import { Dropdown, DropdownCheckedItem } from 'netlify-cms-ui-default'; import { Dropdown, DropdownCheckedItem } from 'netlify-cms-ui-default';
import { ControlButton } from './ControlButton'; import { ControlButton } from './ControlButton';
const FilterControl = ({ viewFilters, t, onFilterClick, filter }) => { function FilterControl({ viewFilters, t, onFilterClick, filter }) {
const hasActiveFilter = filter const hasActiveFilter = filter
?.valueSeq() ?.valueSeq()
.toJS() .toJS()
@ -33,6 +33,6 @@ const FilterControl = ({ viewFilters, t, onFilterClick, filter }) => {
})} })}
</Dropdown> </Dropdown>
); );
}; }
export default translate()(FilterControl); export default translate()(FilterControl);

View File

@ -3,7 +3,7 @@ import { translate } from 'react-polyglot';
import { Dropdown, DropdownItem } from 'netlify-cms-ui-default'; import { Dropdown, DropdownItem } from 'netlify-cms-ui-default';
import { ControlButton } from './ControlButton'; import { ControlButton } from './ControlButton';
const GroupControl = ({ viewGroups, t, onGroupClick, group }) => { function GroupControl({ viewGroups, t, onGroupClick, group }) {
const hasActiveGroup = group const hasActiveGroup = group
?.valueSeq() ?.valueSeq()
.toJS() .toJS()
@ -33,6 +33,6 @@ const GroupControl = ({ viewGroups, t, onGroupClick, group }) => {
})} })}
</Dropdown> </Dropdown>
); );
}; }
export default translate()(GroupControl); export default translate()(GroupControl);

View File

@ -66,14 +66,14 @@ const TreeNavLink = styled(NavLink)`
`}; `};
`; `;
const getNodeTitle = node => { function getNodeTitle(node) {
const title = node.isRoot const title = node.isRoot
? node.title ? node.title
: node.children.find(c => !c.isDir && c.title)?.title || node.title; : node.children.find(c => !c.isDir && c.title)?.title || node.title;
return title; return title;
}; }
const TreeNode = props => { function TreeNode(props) {
const { collection, treeData, depth = 0, onToggle } = props; const { collection, treeData, depth = 0, onToggle } = props;
const collectionName = collection.get('name'); const collectionName = collection.get('name');
@ -118,7 +118,7 @@ const TreeNode = props => {
</React.Fragment> </React.Fragment>
); );
}); });
}; }
TreeNode.propTypes = { TreeNode.propTypes = {
collection: ImmutablePropTypes.map.isRequired, collection: ImmutablePropTypes.map.isRequired,
@ -127,18 +127,18 @@ TreeNode.propTypes = {
onToggle: PropTypes.func.isRequired, onToggle: PropTypes.func.isRequired,
}; };
export const walk = (treeData, callback) => { export function walk(treeData, callback) {
const traverse = children => { function traverse(children) {
for (const child of children) { for (const child of children) {
callback(child); callback(child);
traverse(child.children); traverse(child.children);
} }
}; }
return traverse(treeData); return traverse(treeData);
}; }
export const getTreeData = (collection, entries) => { export function getTreeData(collection, entries) {
const collectionFolder = collection.get('folder'); const collectionFolder = collection.get('folder');
const rootFolder = '/'; const rootFolder = '/';
const entriesObj = entries const entriesObj = entries
@ -200,7 +200,7 @@ export const getTreeData = (collection, entries) => {
return acc; return acc;
}, {}); }, {});
const reducer = (acc, value) => { function reducer(acc, value) {
const node = value; const node = value;
let children = []; let children = [];
if (parentsToChildren[node.path]) { if (parentsToChildren[node.path]) {
@ -209,17 +209,17 @@ export const getTreeData = (collection, entries) => {
acc.push({ ...node, children }); acc.push({ ...node, children });
return acc; return acc;
}; }
const treeData = parentsToChildren[''].reduce(reducer, []); const treeData = parentsToChildren[''].reduce(reducer, []);
return treeData; return treeData;
}; }
export const updateNode = (treeData, node, callback) => { export function updateNode(treeData, node, callback) {
let stop = false; let stop = false;
const updater = nodes => { function updater(nodes) {
if (stop) { if (stop) {
return nodes; return nodes;
} }
@ -232,10 +232,10 @@ export const updateNode = (treeData, node, callback) => {
} }
nodes.forEach(node => updater(node.children)); nodes.forEach(node => updater(node.children));
return nodes; return nodes;
}; }
return updater([...treeData]); return updater([...treeData]);
}; }
export class NestedCollection extends React.Component { export class NestedCollection extends React.Component {
static propTypes = { static propTypes = {

View File

@ -28,7 +28,7 @@ const sortIconDirections = {
[SortDirection.Descending]: 'down', [SortDirection.Descending]: 'down',
}; };
const SortControl = ({ t, fields, onSortClick, sort }) => { function SortControl({ t, fields, onSortClick, sort }) {
const hasActiveSort = sort const hasActiveSort = sort
?.valueSeq() ?.valueSeq()
.toJS() .toJS()
@ -62,6 +62,6 @@ const SortControl = ({ t, fields, onSortClick, sort }) => {
})} })}
</Dropdown> </Dropdown>
); );
}; }
export default translate()(SortControl); export default translate()(SortControl);

View File

@ -27,7 +27,7 @@ const ViewControlsButton = styled.button`
} }
`; `;
const ViewStyleControl = ({ viewStyle, onChangeViewStyle }) => { function ViewStyleControl({ viewStyle, onChangeViewStyle }) {
return ( return (
<ViewControlsSection> <ViewControlsSection>
<ViewControlsButton <ViewControlsButton
@ -44,6 +44,6 @@ const ViewStyleControl = ({ viewStyle, onChangeViewStyle }) => {
</ViewControlsButton> </ViewControlsButton>
</ViewControlsSection> </ViewControlsSection>
); );
}; }
export default ViewStyleControl; export default ViewStyleControl;

View File

@ -13,12 +13,13 @@ jest.mock('../Sidebar', () => 'mock-sidebar');
const middlewares = []; const middlewares = [];
const mockStore = configureStore(middlewares); const mockStore = configureStore(middlewares);
const renderWithRedux = (component, { store } = {}) => { function renderWithRedux(component, { store } = {}) {
function Wrapper({ children }) { function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>; return <Provider store={store}>{children}</Provider>;
} }
return render(component, { wrapper: Wrapper }); return render(component, { wrapper: Wrapper });
}; }
describe('Collection', () => { describe('Collection', () => {
const collection = fromJS({ const collection = fromJS({

View File

@ -22,12 +22,13 @@ jest.mock('netlify-cms-ui-default', () => {
const middlewares = []; const middlewares = [];
const mockStore = configureStore(middlewares); const mockStore = configureStore(middlewares);
const renderWithRedux = (component, { store } = {}) => { function renderWithRedux(component, { store } = {}) {
function Wrapper({ children }) { function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>; return <Provider store={store}>{children}</Provider>;
} }
return render(component, { wrapper: Wrapper }); return render(component, { wrapper: Wrapper });
}; }
describe('NestedCollection', () => { describe('NestedCollection', () => {
const collection = fromJS({ const collection = fromJS({

View File

@ -95,7 +95,7 @@ export const ControlHint = styled.p`
transition: color ${transitions.main}; transition: color ${transitions.main};
`; `;
const LabelComponent = ({ field, isActive, hasErrors, uniqueFieldId, isFieldOptional, t }) => { function LabelComponent({ field, isActive, hasErrors, uniqueFieldId, isFieldOptional, t }) {
const label = `${field.get('label', field.get('name'))}`; const label = `${field.get('label', field.get('name'))}`;
const labelComponent = ( const labelComponent = (
<FieldLabel isActive={isActive} hasErrors={hasErrors} htmlFor={uniqueFieldId}> <FieldLabel isActive={isActive} hasErrors={hasErrors} htmlFor={uniqueFieldId}>
@ -104,7 +104,7 @@ const LabelComponent = ({ field, isActive, hasErrors, uniqueFieldId, isFieldOpti
); );
return labelComponent; return labelComponent;
}; }
class EditorControl extends React.Component { class EditorControl extends React.Component {
static propTypes = { static propTypes = {
@ -334,13 +334,13 @@ class EditorControl extends React.Component {
} }
} }
const mapStateToProps = state => { function mapStateToProps(state) {
const { collections, entryDraft } = state; const { collections, entryDraft } = state;
const entry = entryDraft.get('entry'); const entry = entryDraft.get('entry');
const collection = collections.get(entryDraft.getIn(['entry', 'collection'])); const collection = collections.get(entryDraft.getIn(['entry', 'collection']));
const isLoadingAsset = selectIsLoadingAsset(state.medias); const isLoadingAsset = selectIsLoadingAsset(state.medias);
const loadEntry = async (collectionName, slug) => { async function loadEntry(collectionName, slug) {
const targetCollection = collections.get(collectionName); const targetCollection = collections.get(collectionName);
if (targetCollection) { if (targetCollection) {
const loadedEntry = await tryLoadEntry(state, targetCollection, slug); const loadedEntry = await tryLoadEntry(state, targetCollection, slug);
@ -348,7 +348,7 @@ const mapStateToProps = state => {
} else { } else {
throw new Error(`Can't find collection '${collectionName}'`); throw new Error(`Can't find collection '${collectionName}'`);
} }
}; }
return { return {
mediaPaths: state.mediaLibrary.get('controlMedia'), mediaPaths: state.mediaLibrary.get('controlMedia'),
@ -361,9 +361,9 @@ const mapStateToProps = state => {
loadEntry, loadEntry,
validateMetaField: (field, value, t) => validateMetaField(state, collection, field, value, t), validateMetaField: (field, value, t) => validateMetaField(state, collection, field, value, t),
}; };
}; }
const mapDispatchToProps = dispatch => { function mapDispatchToProps(dispatch) {
const creators = bindActionCreators( const creators = bindActionCreators(
{ {
openMediaLibrary, openMediaLibrary,
@ -381,16 +381,16 @@ const mapDispatchToProps = dispatch => {
...creators, ...creators,
boundGetAsset: (collection, entry) => boundGetAsset(dispatch, collection, entry), boundGetAsset: (collection, entry) => boundGetAsset(dispatch, collection, entry),
}; };
}; }
const mergeProps = (stateProps, dispatchProps, ownProps) => { function mergeProps(stateProps, dispatchProps, ownProps) {
return { return {
...stateProps, ...stateProps,
...dispatchProps, ...dispatchProps,
...ownProps, ...ownProps,
boundGetAsset: dispatchProps.boundGetAsset(stateProps.collection, stateProps.entry), boundGetAsset: dispatchProps.boundGetAsset(stateProps.collection, stateProps.entry),
}; };
}; }
const ConnectedEditorControl = connect( const ConnectedEditorControl = connect(
mapStateToProps, mapStateToProps,

View File

@ -50,7 +50,7 @@ const StyledDropdown = styled(Dropdown)`
margin-bottom: 20px; margin-bottom: 20px;
`; `;
const LocaleDropdown = ({ locales, selectedLocale, onLocaleChange, t }) => { function LocaleDropdown({ locales, selectedLocale, onLocaleChange, t }) {
return ( return (
<StyledDropdown <StyledDropdown
renderButton={() => { renderButton={() => {
@ -77,9 +77,9 @@ const LocaleDropdown = ({ locales, selectedLocale, onLocaleChange, t }) => {
))} ))}
</StyledDropdown> </StyledDropdown>
); );
}; }
const getFieldValue = ({ field, entry, isTranslatable, locale }) => { function getFieldValue({ field, entry, isTranslatable, locale }) {
if (field.get('meta')) { if (field.get('meta')) {
return entry.getIn(['meta', field.get('name')]); return entry.getIn(['meta', field.get('name')]);
} }
@ -90,7 +90,7 @@ const getFieldValue = ({ field, entry, isTranslatable, locale }) => {
} }
return entry.getIn(['data', field.get('name')]); return entry.getIn(['data', field.get('name')]);
}; }
export default class ControlPane extends React.Component { export default class ControlPane extends React.Component {
state = { state = {

View File

@ -5,14 +5,19 @@ import { Map, List } from 'immutable';
import { oneLine } from 'common-tags'; import { oneLine } from 'common-tags';
import ValidationErrorTypes from 'Constants/validationErrorTypes'; import ValidationErrorTypes from 'Constants/validationErrorTypes';
const truthy = () => ({ error: false }); function truthy() {
return { error: false };
}
const isEmpty = value => function isEmpty(value) {
return (
value === null || value === null ||
value === undefined || value === undefined ||
(Object.prototype.hasOwnProperty.call(value, 'length') && value.length === 0) || (Object.prototype.hasOwnProperty.call(value, 'length') && value.length === 0) ||
(value.constructor === Object && Object.keys(value).length === 0) || (value.constructor === Object && Object.keys(value).length === 0) ||
(List.isList(value) && value.size === 0); (List.isList(value) && value.size === 0)
);
}
export default class Widget extends Component { export default class Widget extends Component {
static propTypes = { static propTypes = {

View File

@ -41,7 +41,8 @@ const EditorToggle = styled(IconButton)`
margin-bottom: 12px; margin-bottom: 12px;
`; `;
const ReactSplitPaneGlobalStyles = () => ( function ReactSplitPaneGlobalStyles() {
return (
<Global <Global
styles={css` styles={css`
.Resizer.vertical { .Resizer.vertical {
@ -67,7 +68,8 @@ const ReactSplitPaneGlobalStyles = () => (
} }
`} `}
/> />
); );
}
const StyledSplitPane = styled(SplitPane)` const StyledSplitPane = styled(SplitPane)`
${styles.splitPane}; ${styles.splitPane};
@ -121,13 +123,13 @@ const ViewControls = styled.div`
z-index: ${zIndex.zIndex299}; z-index: ${zIndex.zIndex299};
`; `;
const EditorContent = ({ function EditorContent({
i18nVisible, i18nVisible,
previewVisible, previewVisible,
editor, editor,
editorWithEditor, editorWithEditor,
editorWithPreview, editorWithPreview,
}) => { }) {
if (i18nVisible) { if (i18nVisible) {
return editorWithEditor; return editorWithEditor;
} else if (previewVisible) { } else if (previewVisible) {
@ -135,7 +137,7 @@ const EditorContent = ({
} else { } else {
return <NoPreviewContainer>{editor}</NoPreviewContainer>; return <NoPreviewContainer>{editor}</NoPreviewContainer>;
} }
}; }
function isPreviewEnabled(collection, entry) { function isPreviewEnabled(collection, entry) {
if (collection.get('type') === FILES) { if (collection.get('type') === FILES) {

View File

@ -248,24 +248,24 @@ PreviewPane.propTypes = {
getAsset: PropTypes.func.isRequired, getAsset: PropTypes.func.isRequired,
}; };
const mapStateToProps = state => { function mapStateToProps(state) {
const isLoadingAsset = selectIsLoadingAsset(state.medias); const isLoadingAsset = selectIsLoadingAsset(state.medias);
return { isLoadingAsset, config: state.config }; return { isLoadingAsset, config: state.config };
}; }
const mapDispatchToProps = dispatch => { function mapDispatchToProps(dispatch) {
return { return {
boundGetAsset: (collection, entry) => boundGetAsset(dispatch, collection, entry), boundGetAsset: (collection, entry) => boundGetAsset(dispatch, collection, entry),
}; };
}; }
const mergeProps = (stateProps, dispatchProps, ownProps) => { function mergeProps(stateProps, dispatchProps, ownProps) {
return { return {
...stateProps, ...stateProps,
...dispatchProps, ...dispatchProps,
...ownProps, ...ownProps,
getAsset: dispatchProps.boundGetAsset(ownProps.collection, ownProps.entry), getAsset: dispatchProps.boundGetAsset(ownProps.collection, ownProps.entry),
}; };
}; }
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(PreviewPane); export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(PreviewPane);

View File

@ -3,11 +3,11 @@ import { translate } from 'react-polyglot';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const UnknownControl = ({ field, t }) => { function UnknownControl({ field, t }) {
return ( return (
<div>{t('editor.editorWidgets.unknownControl.noControl', { widget: field.get('widget') })}</div> <div>{t('editor.editorWidgets.unknownControl.noControl', { widget: field.get('widget') })}</div>
); );
}; }
UnknownControl.propTypes = { UnknownControl.propTypes = {
field: ImmutablePropTypes.map, field: ImmutablePropTypes.map,

View File

@ -3,13 +3,13 @@ import { translate } from 'react-polyglot';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const UnknownPreview = ({ field, t }) => { function UnknownPreview({ field, t }) {
return ( return (
<div className="nc-widgetPreview"> <div className="nc-widgetPreview">
{t('editor.editorWidgets.unknownPreview.noPreview', { widget: field.get('widget') })} {t('editor.editorWidgets.unknownPreview.noPreview', { widget: field.get('widget') })}
</div> </div>
); );
}; }
UnknownPreview.propTypes = { UnknownPreview.propTypes = {
field: ImmutablePropTypes.map, field: ImmutablePropTypes.map,

View File

@ -12,11 +12,13 @@ const EmptyMessageContainer = styled.div`
color: ${props => props.isPrivate && colors.textFieldBorder}; color: ${props => props.isPrivate && colors.textFieldBorder};
`; `;
const EmptyMessage = ({ content, isPrivate }) => ( function EmptyMessage({ content, isPrivate }) {
return (
<EmptyMessageContainer isPrivate={isPrivate}> <EmptyMessageContainer isPrivate={isPrivate}>
<h1>{content}</h1> <h1>{content}</h1>
</EmptyMessageContainer> </EmptyMessageContainer>
); );
}
EmptyMessage.propTypes = { EmptyMessage.propTypes = {
content: PropTypes.string.isRequired, content: PropTypes.string.isRequired,

View File

@ -354,7 +354,7 @@ class MediaLibrary extends React.Component {
} }
} }
const mapStateToProps = state => { function mapStateToProps(state) {
const { mediaLibrary } = state; const { mediaLibrary } = state;
const field = mediaLibrary.get('field'); const field = mediaLibrary.get('field');
const mediaLibraryProps = { const mediaLibraryProps = {
@ -377,7 +377,7 @@ const mapStateToProps = state => {
field, field,
}; };
return { ...mediaLibraryProps }; return { ...mediaLibraryProps };
}; }
const mapDispatchToProps = { const mapDispatchToProps = {
loadMedia: loadMediaAction, loadMedia: loadMediaAction,

View File

@ -8,7 +8,7 @@ import { colors } from 'netlify-cms-ui-default';
import { FixedSizeGrid as Grid } from 'react-window'; import { FixedSizeGrid as Grid } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
const CardWrapper = props => { function CardWrapper(props) {
const { const {
rowIndex, rowIndex,
columnIndex, columnIndex,
@ -61,9 +61,9 @@ const CardWrapper = props => {
/> />
</div> </div>
); );
}; }
const VirtualizedGrid = props => { function VirtualizedGrid(props) {
const { mediaItems, setScrollContainerRef } = props; const { mediaItems, setScrollContainerRef } = props;
return ( return (
@ -94,9 +94,9 @@ const VirtualizedGrid = props => {
</AutoSizer> </AutoSizer>
</CardGridContainer> </CardGridContainer>
); );
}; }
const PaginatedGrid = ({ function PaginatedGrid({
setScrollContainerRef, setScrollContainerRef,
mediaItems, mediaItems,
isSelectedFile, isSelectedFile,
@ -112,7 +112,7 @@ const PaginatedGrid = ({
onLoadMore, onLoadMore,
isPaginating, isPaginating,
paginatingMessage, paginatingMessage,
}) => { }) {
return ( return (
<CardGridContainer ref={setScrollContainerRef}> <CardGridContainer ref={setScrollContainerRef}>
<CardGrid> <CardGrid>
@ -141,7 +141,7 @@ const PaginatedGrid = ({
)} )}
</CardGridContainer> </CardGridContainer>
); );
}; }
const CardGridContainer = styled.div` const CardGridContainer = styled.div`
overflow-y: auto; overflow-y: auto;
@ -160,13 +160,13 @@ const PaginatingMessage = styled.h1`
color: ${props => props.isPrivate && colors.textFieldBorder}; color: ${props => props.isPrivate && colors.textFieldBorder};
`; `;
const MediaLibraryCardGrid = props => { function MediaLibraryCardGrid(props) {
const { canLoadMore, isPaginating } = props; const { canLoadMore, isPaginating } = props;
if (canLoadMore || isPaginating) { if (canLoadMore || isPaginating) {
return <PaginatedGrid {...props} />; return <PaginatedGrid {...props} />;
} }
return <VirtualizedGrid {...props} />; return <VirtualizedGrid {...props} />;
}; }
MediaLibraryCardGrid.propTypes = { MediaLibraryCardGrid.propTypes = {
setScrollContainerRef: PropTypes.func.isRequired, setScrollContainerRef: PropTypes.func.isRequired,

View File

@ -28,14 +28,16 @@ const LibraryTitle = styled.h1`
color: ${props => props.isPrivate && colors.textFieldBorder}; color: ${props => props.isPrivate && colors.textFieldBorder};
`; `;
const MediaLibraryHeader = ({ onClose, title, isPrivate }) => ( function MediaLibraryHeader({ onClose, title, isPrivate }) {
return (
<div> <div>
<CloseButton onClick={onClose}> <CloseButton onClick={onClose}>
<Icon type="close" /> <Icon type="close" />
</CloseButton> </CloseButton>
<LibraryTitle isPrivate={isPrivate}>{title}</LibraryTitle> <LibraryTitle isPrivate={isPrivate}>{title}</LibraryTitle>
</div> </div>
); );
}
MediaLibraryHeader.propTypes = { MediaLibraryHeader.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,

View File

@ -60,7 +60,7 @@ const StyledModal = styled(Modal)`
} }
`; `;
const MediaLibraryModal = ({ function MediaLibraryModal({
isVisible, isVisible,
canInsert, canInsert,
files, files,
@ -91,7 +91,7 @@ const MediaLibraryModal = ({
loadDisplayURL, loadDisplayURL,
displayURLs, displayURLs,
t, t,
}) => { }) {
const filteredFiles = forImage ? handleFilter(files) : files; const filteredFiles = forImage ? handleFilter(files) : files;
const queriedFiles = !dynamicSearch && query ? handleQuery(query, filteredFiles) : filteredFiles; const queriedFiles = !dynamicSearch && query ? handleQuery(query, filteredFiles) : filteredFiles;
const tableData = toTableData(queriedFiles); const tableData = toTableData(queriedFiles);
@ -152,7 +152,7 @@ const MediaLibraryModal = ({
/> />
</StyledModal> </StyledModal>
); );
}; }
export const fileShape = { export const fileShape = {
displayURL: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired, displayURL: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,

View File

@ -35,7 +35,8 @@ const SearchIcon = styled(Icon)`
transform: translate(0, -50%); transform: translate(0, -50%);
`; `;
const MediaLibrarySearch = ({ value, onChange, onKeyDown, placeholder, disabled }) => ( function MediaLibrarySearch({ value, onChange, onKeyDown, placeholder, disabled }) {
return (
<SearchContainer> <SearchContainer>
<SearchIcon type="search" size="small" /> <SearchIcon type="search" size="small" />
<SearchInput <SearchInput
@ -46,7 +47,8 @@ const MediaLibrarySearch = ({ value, onChange, onKeyDown, placeholder, disabled
disabled={disabled} disabled={disabled}
/> />
</SearchContainer> </SearchContainer>
); );
}
MediaLibrarySearch.propTypes = { MediaLibrarySearch.propTypes = {
value: PropTypes.string, value: PropTypes.string,

View File

@ -26,7 +26,7 @@ const ButtonsContainer = styled.div`
flex-shrink: 0; flex-shrink: 0;
`; `;
const MediaLibraryTop = ({ function MediaLibraryTop({
t, t,
onClose, onClose,
privateUpload, privateUpload,
@ -44,7 +44,7 @@ const MediaLibraryTop = ({
isPersisting, isPersisting,
isDeleting, isDeleting,
selectedFile, selectedFile,
}) => { }) {
const shouldShowButtonLoader = isPersisting || isDeleting; const shouldShowButtonLoader = isPersisting || isDeleting;
const uploadEnabled = !shouldShowButtonLoader; const uploadEnabled = !shouldShowButtonLoader;
const deleteEnabled = !shouldShowButtonLoader && hasSelection; const deleteEnabled = !shouldShowButtonLoader && hasSelection;
@ -110,7 +110,7 @@ const MediaLibraryTop = ({
</RowContainer> </RowContainer>
</LibraryTop> </LibraryTop>
); );
}; }
MediaLibraryTop.propTypes = { MediaLibraryTop.propTypes = {
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,

View File

@ -7,7 +7,7 @@ import {
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export const DragSource = ({ namespace, ...props }) => { export function DragSource({ namespace, ...props }) {
const DragComponent = ReactDNDDragSource( const DragComponent = ReactDNDDragSource(
namespace, namespace,
{ {
@ -23,13 +23,14 @@ export const DragSource = ({ namespace, ...props }) => {
)(({ children, connectDragComponent }) => children(connectDragComponent)); )(({ children, connectDragComponent }) => children(connectDragComponent));
return React.createElement(DragComponent, props, props.children); return React.createElement(DragComponent, props, props.children);
}; }
DragSource.propTypes = { DragSource.propTypes = {
namespace: PropTypes.any.isRequired, namespace: PropTypes.any.isRequired,
children: PropTypes.func.isRequired, children: PropTypes.func.isRequired,
}; };
export const DropTarget = ({ onDrop, namespace, ...props }) => { export function DropTarget({ onDrop, namespace, ...props }) {
const DropComponent = ReactDNDDropTarget( const DropComponent = ReactDNDDropTarget(
namespace, namespace,
{ {
@ -44,11 +45,14 @@ export const DropTarget = ({ onDrop, namespace, ...props }) => {
)(({ children, connectDropTarget, isHovered }) => children(connectDropTarget, { isHovered })); )(({ children, connectDropTarget, isHovered }) => children(connectDropTarget, { isHovered }));
return React.createElement(DropComponent, props, props.children); return React.createElement(DropComponent, props, props.children);
}; }
DropTarget.propTypes = { DropTarget.propTypes = {
onDrop: PropTypes.func.isRequired, onDrop: PropTypes.func.isRequired,
namespace: PropTypes.any.isRequired, namespace: PropTypes.any.isRequired,
children: PropTypes.func.isRequired, children: PropTypes.func.isRequired,
}; };
export const HTML5DragDrop = component => ReactDNDDragDropContext(ReactDNDHTML5Backend)(component); export function HTML5DragDrop(component) {
return ReactDNDDragDropContext(ReactDNDHTML5Backend)(component);
}

View File

@ -10,7 +10,9 @@ import { localForage } from 'netlify-cms-lib-util';
import { buttons, colors } from 'netlify-cms-ui-default'; import { buttons, colors } from 'netlify-cms-ui-default';
const ISSUE_URL = 'https://github.com/netlify/netlify-cms/issues/new?'; const ISSUE_URL = 'https://github.com/netlify/netlify-cms/issues/new?';
const getIssueTemplate = ({ version, provider, browser, config }) => `
function getIssueTemplate({ version, provider, browser, config }) {
return `
**Describe the bug** **Describe the bug**
**To Reproduce** **To Reproduce**
@ -31,8 +33,9 @@ ${config}
**Additional context** **Additional context**
`; `;
}
const buildIssueTemplate = ({ config }) => { function buildIssueTemplate({ config }) {
let version = ''; let version = '';
if (typeof NETLIFY_CMS_VERSION === 'string') { if (typeof NETLIFY_CMS_VERSION === 'string') {
version = `netlify-cms@${NETLIFY_CMS_VERSION}`; version = `netlify-cms@${NETLIFY_CMS_VERSION}`;
@ -47,9 +50,9 @@ const buildIssueTemplate = ({ config }) => {
}); });
return template; return template;
}; }
const buildIssueUrl = ({ title, config }) => { function buildIssueUrl({ title, config }) {
try { try {
const body = buildIssueTemplate({ config }); const body = buildIssueTemplate({ config });
@ -63,7 +66,7 @@ const buildIssueUrl = ({ title, config }) => {
console.log(e); console.log(e);
return `${ISSUE_URL}template=bug_report.md`; return `${ISSUE_URL}template=bug_report.md`;
} }
}; }
const ErrorBoundaryContainer = styled.div` const ErrorBoundaryContainer = styled.div`
padding: 40px; padding: 40px;
@ -107,7 +110,7 @@ const CopyButton = styled.button`
margin: 12px 0; margin: 12px 0;
`; `;
const RecoveredEntry = ({ entry, t }) => { function RecoveredEntry({ entry, t }) {
console.log(entry); console.log(entry);
return ( return (
<> <>
@ -122,7 +125,7 @@ const RecoveredEntry = ({ entry, t }) => {
</pre> </pre>
</> </>
); );
}; }
export class ErrorBoundary extends React.Component { export class ErrorBoundary extends React.Component {
static propTypes = { static propTypes = {

View File

@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export const FileUploadButton = ({ label, imagesOnly, onChange, disabled, className }) => ( export function FileUploadButton({ label, imagesOnly, onChange, disabled, className }) {
return (
<label className={`nc-fileUploadButton ${className || ''}`}> <label className={`nc-fileUploadButton ${className || ''}`}>
<span>{label}</span> <span>{label}</span>
<input <input
@ -11,7 +12,8 @@ export const FileUploadButton = ({ label, imagesOnly, onChange, disabled, classN
disabled={disabled} disabled={disabled}
/> />
</label> </label>
); );
}
FileUploadButton.propTypes = { FileUploadButton.propTypes = {
className: PropTypes.string, className: PropTypes.string,

View File

@ -4,7 +4,8 @@ import { css, Global, ClassNames } from '@emotion/core';
import ReactModal from 'react-modal'; import ReactModal from 'react-modal';
import { transitions, shadows, lengths, zIndex } from 'netlify-cms-ui-default'; import { transitions, shadows, lengths, zIndex } from 'netlify-cms-ui-default';
const ReactModalGlobalStyles = () => ( function ReactModalGlobalStyles() {
return (
<Global <Global
styles={css` styles={css`
.ReactModal__Body--open { .ReactModal__Body--open {
@ -12,7 +13,8 @@ const ReactModalGlobalStyles = () => (
} }
`} `}
/> />
); );
}
const styleStrings = { const styleStrings = {
modalBody: ` modalBody: `

View File

@ -46,14 +46,20 @@ const AppHeaderTestRepoIndicator = styled.a`
padding: 10px 16px; padding: 10px 16px;
`; `;
const Avatar = ({ imageUrl }) => function Avatar({ imageUrl }) {
imageUrl ? <AvatarImage src={imageUrl} /> : <AvatarPlaceholderIcon type="user" size="large" />; return imageUrl ? (
<AvatarImage src={imageUrl} />
) : (
<AvatarPlaceholderIcon type="user" size="large" />
);
}
Avatar.propTypes = { Avatar.propTypes = {
imageUrl: PropTypes.string, imageUrl: PropTypes.string,
}; };
const SettingsDropdown = ({ displayUrl, isTestRepo, imageUrl, onLogoutClick, t }) => ( function SettingsDropdown({ displayUrl, isTestRepo, imageUrl, onLogoutClick, t }) {
return (
<React.Fragment> <React.Fragment>
{isTestRepo && ( {isTestRepo && (
<AppHeaderTestRepoIndicator <AppHeaderTestRepoIndicator
@ -82,7 +88,8 @@ const SettingsDropdown = ({ displayUrl, isTestRepo, imageUrl, onLogoutClick, t }
<DropdownItem label={t('ui.settingsDropdown.logOut')} onClick={onLogoutClick} /> <DropdownItem label={t('ui.settingsDropdown.logOut')} onClick={onLogoutClick} />
</Dropdown> </Dropdown>
</React.Fragment> </React.Fragment>
); );
}
SettingsDropdown.propTypes = { SettingsDropdown.propTypes = {
displayUrl: PropTypes.string, displayUrl: PropTypes.string,

View File

@ -6,7 +6,8 @@ import { translate } from 'react-polyglot';
import reduxNotificationsStyles from 'redux-notifications/lib/styles.css'; import reduxNotificationsStyles from 'redux-notifications/lib/styles.css';
import { shadows, colors, lengths, zIndex } from 'netlify-cms-ui-default'; import { shadows, colors, lengths, zIndex } from 'netlify-cms-ui-default';
const ReduxNotificationsGlobalStyles = () => ( function ReduxNotificationsGlobalStyles() {
return (
<Global <Global
styles={css` styles={css`
${reduxNotificationsStyles}; ${reduxNotificationsStyles};
@ -17,7 +18,8 @@ const ReduxNotificationsGlobalStyles = () => (
} }
`} `}
/> />
); );
}
const styles = { const styles = {
toast: css` toast: css`
@ -47,12 +49,14 @@ const styles = {
`, `,
}; };
const Toast = ({ kind, message, t }) => ( function Toast({ kind, message, t }) {
return (
<div css={[styles.toast, styles[kind]]}> <div css={[styles.toast, styles[kind]]}>
<ReduxNotificationsGlobalStyles /> <ReduxNotificationsGlobalStyles />
{t(message.key, { details: message.details })} {t(message.key, { details: message.details })}
</div> </div>
); );
}
Toast.propTypes = { Toast.propTypes = {
kind: PropTypes.oneOf(['info', 'success', 'warning', 'danger']).isRequired, kind: PropTypes.oneOf(['info', 'success', 'warning', 'danger']).isRequired,

View File

@ -4,9 +4,9 @@ import { render } from '@testing-library/react';
import { fromJS } from 'immutable'; import { fromJS } from 'immutable';
import { oneLineTrim } from 'common-tags'; import { oneLineTrim } from 'common-tags';
const WithError = () => { function WithError() {
throw new Error('Some unknown error'); throw new Error('Some unknown error');
}; }
jest.spyOn(console, 'error').mockImplementation(() => ({})); jest.spyOn(console, 'error').mockImplementation(() => ({}));

View File

@ -98,7 +98,7 @@ const WorkflowCardContainer = styled.div`
} }
`; `;
const lastChangePhraseKey = (date, author) => { function lastChangePhraseKey(date, author) {
if (date && author) { if (date && author) {
return 'lastChange'; return 'lastChange';
} else if (date) { } else if (date) {
@ -106,7 +106,7 @@ const lastChangePhraseKey = (date, author) => {
} else if (author) { } else if (author) {
return 'lastChangeNoDate'; return 'lastChangeNoDate';
} }
}; }
const CardDate = translate()(({ t, date, author }) => { const CardDate = translate()(({ t, date, author }) => {
const key = lastChangePhraseKey(date, author); const key = lastChangePhraseKey(date, author);
@ -117,7 +117,7 @@ const CardDate = translate()(({ t, date, author }) => {
} }
}); });
const WorkflowCard = ({ function WorkflowCard({
collectionLabel, collectionLabel,
title, title,
authorLastChange, authorLastChange,
@ -130,7 +130,8 @@ const WorkflowCard = ({
canPublish, canPublish,
onPublish, onPublish,
t, t,
}) => ( }) {
return (
<WorkflowCardContainer> <WorkflowCardContainer>
<WorkflowLink to={editLink}> <WorkflowLink to={editLink}>
<CardCollection>{collectionLabel}</CardCollection> <CardCollection>{collectionLabel}</CardCollection>
@ -153,7 +154,8 @@ const WorkflowCard = ({
)} )}
</CardButtonContainer> </CardButtonContainer>
</WorkflowCardContainer> </WorkflowCardContainer>
); );
}
WorkflowCard.propTypes = { WorkflowCard.propTypes = {
collectionLabel: PropTypes.string.isRequired, collectionLabel: PropTypes.string.isRequired,

View File

@ -116,7 +116,7 @@ const ColumnCount = styled.p`
// This is a namespace so that we can only drop these elements on a DropTarget with the same // This is a namespace so that we can only drop these elements on a DropTarget with the same
const DNDNamespace = 'cms-workflow'; const DNDNamespace = 'cms-workflow';
const getColumnHeaderText = (columnName, t) => { function getColumnHeaderText(columnName, t) {
switch (columnName) { switch (columnName) {
case 'draft': case 'draft':
return t('workflow.workflowList.draftHeader'); return t('workflow.workflowList.draftHeader');
@ -125,7 +125,7 @@ const getColumnHeaderText = (columnName, t) => {
case 'pending_publish': case 'pending_publish':
return t('workflow.workflowList.readyHeader'); return t('workflow.workflowList.readyHeader');
} }
}; }
class WorkflowList extends React.Component { class WorkflowList extends React.Component {
static propTypes = { static propTypes = {

View File

@ -43,7 +43,7 @@ const i18nField = {
/** /**
* Config for fields in both file and folder collections. * Config for fields in both file and folder collections.
*/ */
const fieldsConfig = () => { function fieldsConfig() {
const id = uuid(); const id = uuid();
return { return {
$id: `fields_${id}`, $id: `fields_${id}`,
@ -77,7 +77,7 @@ const fieldsConfig = () => {
}, },
uniqueItemProperties: ['name'], uniqueItemProperties: ['name'],
}; };
}; }
const viewFilters = { const viewFilters = {
type: 'array', type: 'array',
@ -121,7 +121,8 @@ const viewGroups = {
* fix a circular dependency problem for WebPack, * fix a circular dependency problem for WebPack,
* where the imports get resolved asynchronously. * where the imports get resolved asynchronously.
*/ */
const getConfigSchema = () => ({ function getConfigSchema() {
return {
type: 'object', type: 'object',
properties: { properties: {
backend: { backend: {
@ -315,7 +316,8 @@ const getConfigSchema = () => ({
}, },
required: ['backend', 'collections'], required: ['backend', 'collections'],
anyOf: [{ required: ['media_folder'] }, { required: ['media_library'] }], anyOf: [{ required: ['media_folder'] }, { required: ['media_library'] }],
}); };
}
function getWidgetSchemas() { function getWidgetSchemas() {
const schemas = getWidgets().map(widget => ({ [widget.name]: widget.schema })); const schemas = getWidgets().map(widget => ({ [widget.name]: widget.schema }));

View File

@ -28,8 +28,8 @@ export const extensionFormatters = {
html: FrontmatterInfer, html: FrontmatterInfer,
}; };
const formatByName = (name, customDelimiter) => function formatByName(name, customDelimiter) {
({ return {
yml: yamlFormatter, yml: yamlFormatter,
yaml: yamlFormatter, yaml: yamlFormatter,
toml: tomlFormatter, toml: tomlFormatter,
@ -38,7 +38,8 @@ const formatByName = (name, customDelimiter) =>
'json-frontmatter': frontmatterJSON(customDelimiter), 'json-frontmatter': frontmatterJSON(customDelimiter),
'toml-frontmatter': frontmatterTOML(customDelimiter), 'toml-frontmatter': frontmatterTOML(customDelimiter),
'yaml-frontmatter': frontmatterYAML(customDelimiter), 'yaml-frontmatter': frontmatterYAML(customDelimiter),
}[name]); }[name];
}
export function resolveFormat(collection, entry) { export function resolveFormat(collection, entry) {
// Check for custom delimiter // Check for custom delimiter

View File

@ -51,12 +51,13 @@ function inferFrontmatterFormat(str) {
} }
} }
export const getFormatOpts = format => export function getFormatOpts(format) {
({ return {
yaml: { language: 'yaml', delimiters: '---' }, yaml: { language: 'yaml', delimiters: '---' },
toml: { language: 'toml', delimiters: '+++' }, toml: { language: 'toml', delimiters: '+++' },
json: { language: 'json', delimiters: ['{', '}'] }, json: { language: 'json', delimiters: ['{', '}'] },
}[format]); }[format];
}
class FrontmatterFormatter { class FrontmatterFormatter {
constructor(format, customDelimiter) { constructor(format, customDelimiter) {
@ -99,6 +100,15 @@ class FrontmatterFormatter {
} }
export const FrontmatterInfer = new FrontmatterFormatter(); export const FrontmatterInfer = new FrontmatterFormatter();
export const frontmatterYAML = customDelimiter => new FrontmatterFormatter('yaml', customDelimiter);
export const frontmatterTOML = customDelimiter => new FrontmatterFormatter('toml', customDelimiter); export function frontmatterYAML(customDelimiter) {
export const frontmatterJSON = customDelimiter => new FrontmatterFormatter('json', customDelimiter); return new FrontmatterFormatter('yaml', customDelimiter);
}
export function frontmatterTOML(customDelimiter) {
return new FrontmatterFormatter('toml', customDelimiter);
}
export function frontmatterJSON(customDelimiter) {
return new FrontmatterFormatter('json', customDelimiter);
}

View File

@ -1,8 +1,10 @@
export const sortKeys = (sortedKeys = [], selector = a => a) => (a, b) => { export function sortKeys(sortedKeys = [], selector = a => a) {
return (a, b) => {
const idxA = sortedKeys.indexOf(selector(a)); const idxA = sortedKeys.indexOf(selector(a));
const idxB = sortedKeys.indexOf(selector(b)); const idxB = sortedKeys.indexOf(selector(b));
if (idxA === -1 || idxB === -1) return 0; if (idxA === -1 || idxB === -1) return 0;
if (idxA > idxB) return 1; if (idxA > idxB) return 1;
if (idxA < idxB) return -1; if (idxA < idxB) return -1;
return 0; return 0;
}; };
}

View File

@ -4,7 +4,7 @@ import moment from 'moment';
import AssetProxy from 'ValueObjects/AssetProxy'; import AssetProxy from 'ValueObjects/AssetProxy';
import { sortKeys } from './helpers'; import { sortKeys } from './helpers';
const outputReplacer = (key, value) => { function outputReplacer(key, value) {
if (moment.isMoment(value)) { if (moment.isMoment(value)) {
return value.format(value._f); return value.format(value._f);
} }
@ -17,7 +17,7 @@ const outputReplacer = (key, value) => {
} }
// Return `false` to use default (`undefined` would delete key). // Return `false` to use default (`undefined` would delete key).
return false; return false;
}; }
export default { export default {
fromFile(content) { fromFile(content) {

View File

@ -1,7 +1,7 @@
import yaml from 'yaml'; import yaml from 'yaml';
import { sortKeys } from './helpers'; import { sortKeys } from './helpers';
const addComments = (items, comments, prefix = '') => { function addComments(items, comments, prefix = '') {
items.forEach(item => { items.forEach(item => {
if (item.key !== undefined) { if (item.key !== undefined) {
const itemKey = item.key.toString(); const itemKey = item.key.toString();
@ -15,7 +15,7 @@ const addComments = (items, comments, prefix = '') => {
} }
} }
}); });
}; }
const timestampTag = { const timestampTag = {
identify: value => value instanceof Date, identify: value => value instanceof Date,

View File

@ -41,12 +41,12 @@ type Options = {
authorName?: string; authorName?: string;
}; };
export const commitMessageFormatter = ( export function commitMessageFormatter(
type: string, type: string,
config: Config, config: Config,
{ slug, path, collection, authorLogin, authorName }: Options, { slug, path, collection, authorLogin, authorName }: Options,
isOpenAuthoring?: boolean, isOpenAuthoring?: boolean,
) => { ) {
const templates = commitMessageTemplates.merge( const templates = commitMessageTemplates.merge(
config.getIn(['backend', 'commit_messages'], Map<string, string>()), config.getIn(['backend', 'commit_messages'], Map<string, string>()),
); );
@ -88,9 +88,9 @@ export const commitMessageFormatter = (
}); });
return message; return message;
}; }
export const prepareSlug = (slug: string) => { export function prepareSlug(slug: string) {
return ( return (
slug slug
.trim() .trim()
@ -103,20 +103,20 @@ export const prepareSlug = (slug: string) => {
// Replace periods with dashes. // Replace periods with dashes.
.replace(/[.]/g, '-') .replace(/[.]/g, '-')
); );
}; }
export const getProcessSegment = (slugConfig: SlugConfig, ignoreValues: string[] = []) => { export function getProcessSegment(slugConfig: SlugConfig, ignoreValues: string[] = []) {
return (value: string) => return (value: string) =>
ignoreValues.includes(value) ignoreValues.includes(value)
? value ? value
: flow([value => String(value), prepareSlug, partialRight(sanitizeSlug, slugConfig)])(value); : flow([value => String(value), prepareSlug, partialRight(sanitizeSlug, slugConfig)])(value);
}; }
export const slugFormatter = ( export function slugFormatter(
collection: Collection, collection: Collection,
entryData: Map<string, unknown>, entryData: Map<string, unknown>,
slugConfig: SlugConfig, slugConfig: SlugConfig,
) => { ) {
const slugTemplate = collection.get('slug') || '{{slug}}'; const slugTemplate = collection.get('slug') || '{{slug}}';
const identifier = entryData.getIn(keyToPathArray(selectIdentifier(collection) as string)); const identifier = entryData.getIn(keyToPathArray(selectIdentifier(collection) as string));
@ -138,15 +138,15 @@ export const slugFormatter = (
value === slug ? value : processSegment(value), value === slug ? value : processSegment(value),
); );
} }
}; }
export const previewUrlFormatter = ( export function previewUrlFormatter(
baseUrl: string, baseUrl: string,
collection: Collection, collection: Collection,
slug: string, slug: string,
slugConfig: SlugConfig, slugConfig: SlugConfig,
entry: EntryMap, entry: EntryMap,
) => { ) {
/** /**
* Preview URL can't be created without `baseUrl`. This makes preview URLs * Preview URL can't be created without `baseUrl`. This makes preview URLs
* optional for backends that don't support them. * optional for backends that don't support them.
@ -160,12 +160,13 @@ export const previewUrlFormatter = (
const isFileCollection = collection.get('type') === FILES; const isFileCollection = collection.get('type') === FILES;
const file = isFileCollection ? getFileFromSlug(collection, entry.get('slug')) : undefined; const file = isFileCollection ? getFileFromSlug(collection, entry.get('slug')) : undefined;
const getPathTemplate = () => { function getPathTemplate() {
return file?.get('preview_path') ?? collection.get('preview_path'); return file?.get('preview_path') ?? collection.get('preview_path');
}; }
const getDateField = () => {
function getDateField() {
return file?.get('preview_path_date_field') ?? collection.get('preview_path_date_field'); return file?.get('preview_path_date_field') ?? collection.get('preview_path_date_field');
}; }
/** /**
* If a `previewPath` is provided for the collection/file, use it to construct the * If a `previewPath` is provided for the collection/file, use it to construct the
@ -209,13 +210,9 @@ export const previewUrlFormatter = (
const previewPath = trimStart(compiledPath, ' /'); const previewPath = trimStart(compiledPath, ' /');
return `${basePath}/${previewPath}`; return `${basePath}/${previewPath}`;
}; }
export const summaryFormatter = ( export function summaryFormatter(summaryTemplate: string, entry: EntryMap, collection: Collection) {
summaryTemplate: string,
entry: EntryMap,
collection: Collection,
) => {
let entryData = entry.get('data'); let entryData = entry.get('data');
const date = const date =
parseDateFromEntry( parseDateFromEntry(
@ -234,16 +231,16 @@ export const summaryFormatter = (
} }
const summary = compileStringTemplate(summaryTemplate, date, identifier, entryData); const summary = compileStringTemplate(summaryTemplate, date, identifier, entryData);
return summary; return summary;
}; }
export const folderFormatter = ( export function folderFormatter(
folderTemplate: string, folderTemplate: string,
entry: EntryMap | undefined, entry: EntryMap | undefined,
collection: Collection, collection: Collection,
defaultFolder: string, defaultFolder: string,
folderKey: string, folderKey: string,
slugConfig: SlugConfig, slugConfig: SlugConfig,
) => { ) {
if (!entry || !entry.get('data')) { if (!entry || !entry.get('data')) {
return folderTemplate; return folderTemplate;
} }
@ -268,4 +265,4 @@ export const folderFormatter = (
); );
return mediaFolder; return mediaFolder;
}; }

View File

@ -18,9 +18,9 @@ export enum I18N_FIELD {
NONE = 'none', NONE = 'none',
} }
export const hasI18n = (collection: Collection) => { export function hasI18n(collection: Collection) {
return collection.has(I18N); return collection.has(I18N);
}; }
type I18nInfo = { type I18nInfo = {
locales: string[]; locales: string[];
@ -28,53 +28,53 @@ type I18nInfo = {
structure: I18N_STRUCTURE; structure: I18N_STRUCTURE;
}; };
export const getI18nInfo = (collection: Collection) => { export function getI18nInfo(collection: Collection) {
if (!hasI18n(collection)) { if (!hasI18n(collection)) {
return {}; return {};
} }
const { structure, locales, default_locale: defaultLocale } = collection.get(I18N).toJS(); const { structure, locales, default_locale: defaultLocale } = collection.get(I18N).toJS();
return { structure, locales, defaultLocale } as I18nInfo; return { structure, locales, defaultLocale } as I18nInfo;
}; }
export const getI18nFilesDepth = (collection: Collection, depth: number) => { export function getI18nFilesDepth(collection: Collection, depth: number) {
const { structure } = getI18nInfo(collection) as I18nInfo; const { structure } = getI18nInfo(collection) as I18nInfo;
if (structure === I18N_STRUCTURE.MULTIPLE_FOLDERS) { if (structure === I18N_STRUCTURE.MULTIPLE_FOLDERS) {
return depth + 1; return depth + 1;
} }
return depth; return depth;
}; }
export const isFieldTranslatable = (field: EntryField, locale: string, defaultLocale: string) => { export function isFieldTranslatable(field: EntryField, locale: string, defaultLocale: string) {
const isTranslatable = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.TRANSLATE; const isTranslatable = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.TRANSLATE;
return isTranslatable; return isTranslatable;
}; }
export const isFieldDuplicate = (field: EntryField, locale: string, defaultLocale: string) => { export function isFieldDuplicate(field: EntryField, locale: string, defaultLocale: string) {
const isDuplicate = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.DUPLICATE; const isDuplicate = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.DUPLICATE;
return isDuplicate; return isDuplicate;
}; }
export const isFieldHidden = (field: EntryField, locale: string, defaultLocale: string) => { export function isFieldHidden(field: EntryField, locale: string, defaultLocale: string) {
const isHidden = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.NONE; const isHidden = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.NONE;
return isHidden; return isHidden;
}; }
export const getLocaleDataPath = (locale: string) => { export function getLocaleDataPath(locale: string) {
return [I18N, locale, 'data']; return [I18N, locale, 'data'];
}; }
export const getDataPath = (locale: string, defaultLocale: string) => { export function getDataPath(locale: string, defaultLocale: string) {
const dataPath = locale !== defaultLocale ? getLocaleDataPath(locale) : ['data']; const dataPath = locale !== defaultLocale ? getLocaleDataPath(locale) : ['data'];
return dataPath; return dataPath;
}; }
export const getFilePath = ( export function getFilePath(
structure: I18N_STRUCTURE, structure: I18N_STRUCTURE,
extension: string, extension: string,
path: string, path: string,
slug: string, slug: string,
locale: string, locale: string,
) => { ) {
switch (structure) { switch (structure) {
case I18N_STRUCTURE.MULTIPLE_FOLDERS: case I18N_STRUCTURE.MULTIPLE_FOLDERS:
return path.replace(`/${slug}`, `/${locale}/${slug}`); return path.replace(`/${slug}`, `/${locale}/${slug}`);
@ -84,9 +84,9 @@ export const getFilePath = (
default: default:
return path; return path;
} }
}; }
export const getLocaleFromPath = (structure: I18N_STRUCTURE, extension: string, path: string) => { export function getLocaleFromPath(structure: I18N_STRUCTURE, extension: string, path: string) {
switch (structure) { switch (structure) {
case I18N_STRUCTURE.MULTIPLE_FOLDERS: { case I18N_STRUCTURE.MULTIPLE_FOLDERS: {
const parts = path.split('/'); const parts = path.split('/');
@ -103,23 +103,23 @@ export const getLocaleFromPath = (structure: I18N_STRUCTURE, extension: string,
default: default:
return ''; return '';
} }
}; }
export const getFilePaths = ( export function getFilePaths(
collection: Collection, collection: Collection,
extension: string, extension: string,
path: string, path: string,
slug: string, slug: string,
) => { ) {
const { structure, locales } = getI18nInfo(collection) as I18nInfo; const { structure, locales } = getI18nInfo(collection) as I18nInfo;
const paths = locales.map(locale => const paths = locales.map(locale =>
getFilePath(structure as I18N_STRUCTURE, extension, path, slug, locale), getFilePath(structure as I18N_STRUCTURE, extension, path, slug, locale),
); );
return paths; return paths;
}; }
export const normalizeFilePath = (structure: I18N_STRUCTURE, path: string, locale: string) => { export function normalizeFilePath(structure: I18N_STRUCTURE, path: string, locale: string) {
switch (structure) { switch (structure) {
case I18N_STRUCTURE.MULTIPLE_FOLDERS: case I18N_STRUCTURE.MULTIPLE_FOLDERS:
return path.replace(`${locale}/`, ''); return path.replace(`${locale}/`, '');
@ -129,9 +129,9 @@ export const normalizeFilePath = (structure: I18N_STRUCTURE, path: string, local
default: default:
return path; return path;
} }
}; }
export const getI18nFiles = ( export function getI18nFiles(
collection: Collection, collection: Collection,
extension: string, extension: string,
entryDraft: EntryMap, entryDraft: EntryMap,
@ -139,7 +139,7 @@ export const getI18nFiles = (
path: string, path: string,
slug: string, slug: string,
newPath?: string, newPath?: string,
) => { ) {
const { structure, defaultLocale, locales } = getI18nInfo(collection) as I18nInfo; const { structure, defaultLocale, locales } = getI18nInfo(collection) as I18nInfo;
if (structure === I18N_STRUCTURE.SINGLE_FILE) { if (structure === I18N_STRUCTURE.SINGLE_FILE) {
@ -176,13 +176,13 @@ export const getI18nFiles = (
}) })
.filter(dataFile => dataFile.raw); .filter(dataFile => dataFile.raw);
return dataFiles; return dataFiles;
}; }
export const getI18nBackup = ( export function getI18nBackup(
collection: Collection, collection: Collection,
entry: EntryMap, entry: EntryMap,
entryToRaw: (entry: EntryMap) => string, entryToRaw: (entry: EntryMap) => string,
) => { ) {
const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo; const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo;
const i18nBackup = locales const i18nBackup = locales
@ -198,26 +198,26 @@ export const getI18nBackup = (
}, {} as Record<string, { raw: string }>); }, {} as Record<string, { raw: string }>);
return i18nBackup; return i18nBackup;
}; }
export const formatI18nBackup = ( export function formatI18nBackup(
i18nBackup: Record<string, { raw: string }>, i18nBackup: Record<string, { raw: string }>,
formatRawData: (raw: string) => EntryValue, formatRawData: (raw: string) => EntryValue,
) => { ) {
const i18n = Object.entries(i18nBackup).reduce((acc, [locale, { raw }]) => { const i18n = Object.entries(i18nBackup).reduce((acc, [locale, { raw }]) => {
const entry = formatRawData(raw); const entry = formatRawData(raw);
return { ...acc, [locale]: { data: entry.data } }; return { ...acc, [locale]: { data: entry.data } };
}, {}); }, {});
return i18n; return i18n;
}; }
const mergeValues = ( function mergeValues(
collection: Collection, collection: Collection,
structure: I18N_STRUCTURE, structure: I18N_STRUCTURE,
defaultLocale: string, defaultLocale: string,
values: { locale: string; value: EntryValue }[], values: { locale: string; value: EntryValue }[],
) => { ) {
let defaultEntry = values.find(e => e.locale === defaultLocale); let defaultEntry = values.find(e => e.locale === defaultLocale);
if (!defaultEntry) { if (!defaultEntry) {
defaultEntry = values[0]; defaultEntry = values[0];
@ -241,9 +241,9 @@ const mergeValues = (
}; };
return entryValue; return entryValue;
}; }
const mergeSingleFileValue = (entryValue: EntryValue, defaultLocale: string, locales: string[]) => { function mergeSingleFileValue(entryValue: EntryValue, defaultLocale: string, locales: string[]) {
const data = entryValue.data[defaultLocale] || {}; const data = entryValue.data[defaultLocale] || {};
const i18n = locales const i18n = locales
.filter(l => l !== defaultLocale) .filter(l => l !== defaultLocale)
@ -259,15 +259,15 @@ const mergeSingleFileValue = (entryValue: EntryValue, defaultLocale: string, loc
i18n, i18n,
raw: '', raw: '',
}; };
}; }
export const getI18nEntry = async ( export async function getI18nEntry(
collection: Collection, collection: Collection,
extension: string, extension: string,
path: string, path: string,
slug: string, slug: string,
getEntryValue: (path: string) => Promise<EntryValue>, getEntryValue: (path: string) => Promise<EntryValue>,
) => { ) {
const { structure, locales, defaultLocale } = getI18nInfo(collection) as I18nInfo; const { structure, locales, defaultLocale } = getI18nInfo(collection) as I18nInfo;
let entryValue: EntryValue; let entryValue: EntryValue;
@ -291,9 +291,9 @@ export const getI18nEntry = async (
} }
return entryValue; return entryValue;
}; }
export const groupEntries = (collection: Collection, extension: string, entries: EntryValue[]) => { export function groupEntries(collection: Collection, extension: string, entries: EntryValue[]) {
const { structure, defaultLocale, locales } = getI18nInfo(collection) as I18nInfo; const { structure, defaultLocale, locales } = getI18nInfo(collection) as I18nInfo;
if (structure === I18N_STRUCTURE.SINGLE_FILE) { if (structure === I18N_STRUCTURE.SINGLE_FILE) {
return entries.map(e => mergeSingleFileValue(e, defaultLocale, locales)); return entries.map(e => mergeSingleFileValue(e, defaultLocale, locales));
@ -315,15 +315,15 @@ export const groupEntries = (collection: Collection, extension: string, entries:
}, [] as EntryValue[]); }, [] as EntryValue[]);
return groupedEntries; return groupedEntries;
}; }
export const getI18nDataFiles = ( export function getI18nDataFiles(
collection: Collection, collection: Collection,
extension: string, extension: string,
path: string, path: string,
slug: string, slug: string,
diffFiles: { path: string; id: string; newFile: boolean }[], diffFiles: { path: string; id: string; newFile: boolean }[],
) => { ) {
const { structure } = getI18nInfo(collection) as I18nInfo; const { structure } = getI18nInfo(collection) as I18nInfo;
if (structure === I18N_STRUCTURE.SINGLE_FILE) { if (structure === I18N_STRUCTURE.SINGLE_FILE) {
return diffFiles; return diffFiles;
@ -339,10 +339,10 @@ export const getI18nDataFiles = (
}, [] as { path: string; id: string; newFile: boolean }[]); }, [] as { path: string; id: string; newFile: boolean }[]);
return dataFiles; return dataFiles;
}; }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export const duplicateDefaultI18nFields = (collection: Collection, dataFields: any) => { export function duplicateDefaultI18nFields(collection: Collection, dataFields: any) {
const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo; const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo;
const i18nFields = Object.fromEntries( const i18nFields = Object.fromEntries(
@ -352,15 +352,15 @@ export const duplicateDefaultI18nFields = (collection: Collection, dataFields: a
); );
return i18nFields; return i18nFields;
}; }
export const duplicateI18nFields = ( export function duplicateI18nFields(
entryDraft: EntryDraft, entryDraft: EntryDraft,
field: EntryField, field: EntryField,
locales: string[], locales: string[],
defaultLocale: string, defaultLocale: string,
fieldPath: string[] = [field.get('name')], fieldPath: string[] = [field.get('name')],
) => { ) {
const value = entryDraft.getIn(['entry', 'data', ...fieldPath]); const value = entryDraft.getIn(['entry', 'data', ...fieldPath]);
if (field.get(I18N) === I18N_FIELD.DUPLICATE) { if (field.get(I18N) === I18N_FIELD.DUPLICATE) {
locales locales
@ -392,21 +392,21 @@ export const duplicateI18nFields = (
} }
return entryDraft; return entryDraft;
}; }
export const getPreviewEntry = (entry: EntryMap, locale: string, defaultLocale: string) => { export function getPreviewEntry(entry: EntryMap, locale: string, defaultLocale: string) {
if (locale === defaultLocale) { if (locale === defaultLocale) {
return entry; return entry;
} }
return entry.set('data', entry.getIn([I18N, locale, 'data'])); return entry.set('data', entry.getIn([I18N, locale, 'data']));
}; }
export const serializeI18n = ( export function serializeI18n(
collection: Collection, collection: Collection,
entry: Entry, entry: Entry,
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
serializeValues: (data: any) => any, serializeValues: (data: any) => any,
) => { ) {
const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo; const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo;
locales locales
@ -417,4 +417,4 @@ export const serializeI18n = (
}); });
return entry; return entry;
}; }

View File

@ -20,7 +20,7 @@ import { getWidgetValueSerializer } from './registry';
* registered deserialization handlers run on entry load, and serialization * registered deserialization handlers run on entry load, and serialization
* handlers run on persist. * handlers run on persist.
*/ */
const runSerializer = (values, fields, method) => { function runSerializer(values, fields, method) {
/** /**
* Reduce the list of fields to a map where keys are field names and values * Reduce the list of fields to a map where keys are field names and values
* are field values, serializing the values of fields whose widgets have * are field values, serializing the values of fields whose widgets have
@ -63,12 +63,12 @@ const runSerializer = (values, fields, method) => {
serializedData = values.mergeDeep(serializedData); serializedData = values.mergeDeep(serializedData);
return serializedData; return serializedData;
}; }
export const serializeValues = (values, fields) => { export function serializeValues(values, fields) {
return runSerializer(values, fields, 'serialize'); return runSerializer(values, fields, 'serialize');
}; }
export const deserializeValues = (values, fields) => { export function deserializeValues(values, fields) {
return runSerializer(values, fields, 'deserialize'); return runSerializer(values, fields, 'deserialize');
}; }

View File

@ -36,8 +36,14 @@ export function stripProtocol(urlString: string) {
*/ */
const uriChars = /[\w\-.~]/i; const uriChars = /[\w\-.~]/i;
const ucsChars = /[\xA0-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}]/u; const ucsChars = /[\xA0-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}]/u;
const validURIChar = (char: string) => uriChars.test(char);
const validIRIChar = (char: string) => uriChars.test(char) || ucsChars.test(char); function validURIChar(char: string) {
return uriChars.test(char);
}
function validIRIChar(char: string) {
return uriChars.test(char) || ucsChars.test(char);
}
export function getCharReplacer(encoding: string, replacement: string) { export function getCharReplacer(encoding: string, replacement: string) {
let validChar: (char: string) => boolean; let validChar: (char: string) => boolean;

View File

@ -18,6 +18,12 @@ interface MediaLibrary {
}) => MediaLibraryInstance; }) => MediaLibraryInstance;
} }
function handleInsert(url: string) {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
return store.dispatch(insertMedia(url, undefined));
}
const initializeMediaLibrary = once(async function initializeMediaLibrary(name, options) { const initializeMediaLibrary = once(async function initializeMediaLibrary(name, options) {
const lib = (getMediaLibrary(name) as unknown) as MediaLibrary | undefined; const lib = (getMediaLibrary(name) as unknown) as MediaLibrary | undefined;
if (!lib) { if (!lib) {
@ -26,9 +32,6 @@ const initializeMediaLibrary = once(async function initializeMediaLibrary(name,
); );
store.dispatch(configFailed(err)); store.dispatch(configFailed(err));
} else { } else {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
const handleInsert = (url: string) => store.dispatch(insertMedia(url, undefined));
const instance = await lib.init({ options, handleInsert }); const instance = await lib.init({ options, handleInsert });
store.dispatch(createMediaLibrary(instance)); store.dispatch(createMediaLibrary(instance));
} }

View File

@ -460,7 +460,9 @@ describe('collections', () => {
], ],
}); });
const updater = field => field.set('default', 'default'); function updater(field) {
return field.set('default', 'default');
}
expect(updateFieldByKey(collection, 'non-existent', updater)).toBe(collection); expect(updateFieldByKey(collection, 'non-existent', updater)).toBe(collection);
expect(updateFieldByKey(collection, 'title', updater)).toEqual( expect(updateFieldByKey(collection, 'title', updater)).toEqual(

View File

@ -22,7 +22,7 @@ import { Backend } from '../backend';
const { keyToPathArray } = stringTemplate; const { keyToPathArray } = stringTemplate;
const collections = (state = null, action: CollectionsAction) => { function collections(state = null, action: CollectionsAction) {
switch (action.type) { switch (action.type) {
case CONFIG_SUCCESS: { case CONFIG_SUCCESS: {
const configCollections = action.payload const configCollections = action.payload
@ -49,7 +49,7 @@ const collections = (state = null, action: CollectionsAction) => {
default: default:
return state; return state;
} }
}; }
const selectors = { const selectors = {
[FOLDER]: { [FOLDER]: {
@ -120,7 +120,7 @@ const selectors = {
}, },
}; };
const getFieldsWithMediaFolders = (fields: EntryField[]) => { function getFieldsWithMediaFolders(fields: EntryField[]) {
const fieldsWithMediaFolders = fields.reduce((acc, f) => { const fieldsWithMediaFolders = fields.reduce((acc, f) => {
if (f.has('media_folder')) { if (f.has('media_folder')) {
acc = [...acc, f]; acc = [...acc, f];
@ -141,16 +141,16 @@ const getFieldsWithMediaFolders = (fields: EntryField[]) => {
}, [] as EntryField[]); }, [] as EntryField[]);
return fieldsWithMediaFolders; return fieldsWithMediaFolders;
}; }
export const getFileFromSlug = (collection: Collection, slug: string) => { export function getFileFromSlug(collection: Collection, slug: string) {
return collection return collection
.get('files') .get('files')
?.toArray() ?.toArray()
.find(f => f.get('name') === slug); .find(f => f.get('name') === slug);
}; }
export const selectFieldsWithMediaFolders = (collection: Collection, slug: string) => { export function selectFieldsWithMediaFolders(collection: Collection, slug: string) {
if (collection.has('folder')) { if (collection.has('folder')) {
const fields = collection.get('fields').toArray(); const fields = collection.get('fields').toArray();
return getFieldsWithMediaFolders(fields); return getFieldsWithMediaFolders(fields);
@ -163,9 +163,9 @@ export const selectFieldsWithMediaFolders = (collection: Collection, slug: strin
} }
return []; return [];
}; }
export const selectMediaFolders = (state: State, collection: Collection, entry: EntryMap) => { export function selectMediaFolders(state: State, collection: Collection, entry: EntryMap) {
const fields = selectFieldsWithMediaFolders(collection, entry.get('slug')); const fields = selectFieldsWithMediaFolders(collection, entry.get('slug'));
const folders = fields.map(f => selectMediaFolder(state.config, collection, entry, f)); const folders = fields.map(f => selectMediaFolder(state.config, collection, entry, f));
if (collection.has('files')) { if (collection.has('files')) {
@ -181,26 +181,41 @@ export const selectMediaFolders = (state: State, collection: Collection, entry:
} }
return Set(folders).toArray(); return Set(folders).toArray();
}; }
export const selectFields = (collection: Collection, slug: string) => export function selectFields(collection: Collection, slug: string) {
selectors[collection.get('type')].fields(collection, slug); return selectors[collection.get('type')].fields(collection, slug);
export const selectFolderEntryExtension = (collection: Collection) => }
selectors[FOLDER].entryExtension(collection);
export const selectFileEntryLabel = (collection: Collection, slug: string) =>
selectors[FILES].entryLabel(collection, slug);
export const selectEntryPath = (collection: Collection, slug: string) =>
selectors[collection.get('type')].entryPath(collection, slug);
export const selectEntrySlug = (collection: Collection, path: string) =>
selectors[collection.get('type')].entrySlug(collection, path);
export const selectAllowNewEntries = (collection: Collection) =>
selectors[collection.get('type')].allowNewEntries(collection);
export const selectAllowDeletion = (collection: Collection) =>
selectors[collection.get('type')].allowDeletion(collection);
export const selectTemplateName = (collection: Collection, slug: string) =>
selectors[collection.get('type')].templateName(collection, slug);
export const getFieldsNames = (fields: EntryField[], prefix = '') => { export function selectFolderEntryExtension(collection: Collection) {
return selectors[FOLDER].entryExtension(collection);
}
export function selectFileEntryLabel(collection: Collection, slug: string) {
return selectors[FILES].entryLabel(collection, slug);
}
export function selectEntryPath(collection: Collection, slug: string) {
return selectors[collection.get('type')].entryPath(collection, slug);
}
export function selectEntrySlug(collection: Collection, path: string) {
return selectors[collection.get('type')].entrySlug(collection, path);
}
export function selectAllowNewEntries(collection: Collection) {
return selectors[collection.get('type')].allowNewEntries(collection);
}
export function selectAllowDeletion(collection: Collection) {
return selectors[collection.get('type')].allowDeletion(collection);
}
export function selectTemplateName(collection: Collection, slug: string) {
return selectors[collection.get('type')].templateName(collection, slug);
}
export function getFieldsNames(fields: EntryField[], prefix = '') {
let names = fields.map(f => `${prefix}${f.get('name')}`); let names = fields.map(f => `${prefix}${f.get('name')}`);
fields.forEach((f, index) => { fields.forEach((f, index) => {
@ -217,9 +232,9 @@ export const getFieldsNames = (fields: EntryField[], prefix = '') => {
}); });
return names; return names;
}; }
export const selectField = (collection: Collection, key: string) => { export function selectField(collection: Collection, key: string) {
const array = keyToPathArray(key); const array = keyToPathArray(key);
let name: string | undefined; let name: string | undefined;
let field; let field;
@ -236,13 +251,13 @@ export const selectField = (collection: Collection, key: string) => {
} }
return field; return field;
}; }
export const traverseFields = ( export function traverseFields(
fields: List<EntryField>, fields: List<EntryField>,
updater: (field: EntryField) => EntryField, updater: (field: EntryField) => EntryField,
done = () => false, done = () => false,
) => { ) {
if (done()) { if (done()) {
return fields; return fields;
} }
@ -268,20 +283,21 @@ export const traverseFields = (
.toList() as List<EntryField>; .toList() as List<EntryField>;
return fields; return fields;
}; }
export const updateFieldByKey = ( export function updateFieldByKey(
collection: Collection, collection: Collection,
key: string, key: string,
updater: (field: EntryField) => EntryField, updater: (field: EntryField) => EntryField,
) => { ) {
const selected = selectField(collection, key); const selected = selectField(collection, key);
if (!selected) { if (!selected) {
return collection; return collection;
} }
let updated = false; let updated = false;
const updateAndBreak = (f: EntryField) => {
function updateAndBreak(f: EntryField) {
const field = f as EntryField; const field = f as EntryField;
if (field === selected) { if (field === selected) {
updated = true; updated = true;
@ -289,7 +305,7 @@ export const updateFieldByKey = (
} else { } else {
return field; return field;
} }
}; }
collection = collection.set( collection = collection.set(
'fields', 'fields',
@ -297,18 +313,18 @@ export const updateFieldByKey = (
); );
return collection; return collection;
}; }
export const selectIdentifier = (collection: Collection) => { export function selectIdentifier(collection: Collection) {
const identifier = collection.get('identifier_field'); const identifier = collection.get('identifier_field');
const identifierFields = identifier ? [identifier, ...IDENTIFIER_FIELDS] : IDENTIFIER_FIELDS; const identifierFields = identifier ? [identifier, ...IDENTIFIER_FIELDS] : IDENTIFIER_FIELDS;
const fieldNames = getFieldsNames(collection.get('fields', List<EntryField>()).toArray()); const fieldNames = getFieldsNames(collection.get('fields', List<EntryField>()).toArray());
return identifierFields.find(id => return identifierFields.find(id =>
fieldNames.find(name => name?.toLowerCase().trim() === id.toLowerCase().trim()), fieldNames.find(name => name?.toLowerCase().trim() === id.toLowerCase().trim()),
); );
}; }
export const selectInferedField = (collection: Collection, fieldName: string) => { export function selectInferedField(collection: Collection, fieldName: string) {
if (fieldName === 'title' && collection.get('identifier_field')) { if (fieldName === 'title' && collection.get('identifier_field')) {
return selectIdentifier(collection); return selectIdentifier(collection);
} }
@ -355,9 +371,9 @@ export const selectInferedField = (collection: Collection, fieldName: string) =>
} }
return null; return null;
}; }
export const selectEntryCollectionTitle = (collection: Collection, entry: EntryMap) => { export function selectEntryCollectionTitle(collection: Collection, entry: EntryMap) {
// prefer formatted summary over everything else // prefer formatted summary over everything else
const summaryTemplate = collection.get('summary'); const summaryTemplate = collection.get('summary');
if (summaryTemplate) return summaryFormatter(summaryTemplate, entry, collection); if (summaryTemplate) return summaryFormatter(summaryTemplate, entry, collection);
@ -372,16 +388,16 @@ export const selectEntryCollectionTitle = (collection: Collection, entry: EntryM
const entryData = entry.get('data'); const entryData = entry.get('data');
const titleField = selectInferedField(collection, 'title'); const titleField = selectInferedField(collection, 'title');
return titleField && entryData.getIn(keyToPathArray(titleField)); return titleField && entryData.getIn(keyToPathArray(titleField));
}; }
export const COMMIT_AUTHOR = 'commit_author'; export const COMMIT_AUTHOR = 'commit_author';
export const COMMIT_DATE = 'commit_date'; export const COMMIT_DATE = 'commit_date';
export const selectDefaultSortableFields = ( export function selectDefaultSortableFields(
collection: Collection, collection: Collection,
backend: Backend, backend: Backend,
hasIntegration: boolean, hasIntegration: boolean,
) => { ) {
let defaultSortable = SORTABLE_FIELDS.map((type: string) => { let defaultSortable = SORTABLE_FIELDS.map((type: string) => {
const field = selectInferedField(collection, type); const field = selectInferedField(collection, type);
if (backend.isGitBackend() && type === 'author' && !field && !hasIntegration) { if (backend.isGitBackend() && type === 'author' && !field && !hasIntegration) {
@ -397,9 +413,9 @@ export const selectDefaultSortableFields = (
} }
return defaultSortable as string[]; return defaultSortable as string[];
}; }
export const selectSortableFields = (collection: Collection, t: (key: string) => string) => { export function selectSortableFields(collection: Collection, t: (key: string) => string) {
const fields = collection const fields = collection
.get('sortable_fields') .get('sortable_fields')
.toArray() .toArray()
@ -418,9 +434,9 @@ export const selectSortableFields = (collection: Collection, t: (key: string) =>
.map(item => ({ ...item.field, key: item.key })); .map(item => ({ ...item.field, key: item.key }));
return fields; return fields;
}; }
export const selectSortDataPath = (collection: Collection, key: string) => { export function selectSortDataPath(collection: Collection, key: string) {
if (key === COMMIT_DATE) { if (key === COMMIT_DATE) {
return 'updatedOn'; return 'updatedOn';
} else if (key === COMMIT_AUTHOR && !selectField(collection, key)) { } else if (key === COMMIT_AUTHOR && !selectField(collection, key)) {
@ -428,19 +444,19 @@ export const selectSortDataPath = (collection: Collection, key: string) => {
} else { } else {
return `data.${key}`; return `data.${key}`;
} }
}; }
export const selectViewFilters = (collection: Collection) => { export function selectViewFilters(collection: Collection) {
const viewFilters = collection.get('view_filters').toJS() as ViewFilter[]; const viewFilters = collection.get('view_filters').toJS() as ViewFilter[];
return viewFilters; return viewFilters;
}; }
export const selectViewGroups = (collection: Collection) => { export function selectViewGroups(collection: Collection) {
const viewGroups = collection.get('view_groups').toJS() as ViewGroup[]; const viewGroups = collection.get('view_groups').toJS() as ViewGroup[];
return viewGroups; return viewGroups;
}; }
export const selectFieldsComments = (collection: Collection, entryMap: EntryMap) => { export function selectFieldsComments(collection: Collection, entryMap: EntryMap) {
let fields: EntryField[] = []; let fields: EntryField[] = [];
if (collection.has('folder')) { if (collection.has('folder')) {
fields = collection.get('fields').toArray(); fields = collection.get('fields').toArray();
@ -458,15 +474,15 @@ export const selectFieldsComments = (collection: Collection, entryMap: EntryMap)
}); });
return comments; return comments;
}; }
export const selectHasMetaPath = (collection: Collection) => { export function selectHasMetaPath(collection: Collection) {
return ( return (
collection.has('folder') && collection.has('folder') &&
collection.get('type') === FOLDER && collection.get('type') === FOLDER &&
collection.has('meta') && collection.has('meta') &&
collection.get('meta')?.has('path') collection.get('meta')?.has('path')
); );
}; }
export default collections; export default collections;

View File

@ -3,12 +3,12 @@ import { connectRouter } from 'connected-react-router';
import { reducer as notifReducer } from 'redux-notifications'; import { reducer as notifReducer } from 'redux-notifications';
import reducers from './index'; import reducers from './index';
const createRootReducer = history => { function createRootReducer(history) {
return combineReducers({ return combineReducers({
...reducers, ...reducers,
notifs: notifReducer, notifs: notifReducer,
router: connectRouter(history), router: connectRouter(history),
}); });
}; }
export default createRootReducer; export default createRootReducer;

View File

@ -5,7 +5,7 @@ import { EDITORIAL_WORKFLOW } from '../constants/publishModes';
const defaultState: Map<string, boolean | string> = Map({ isFetching: true }); const defaultState: Map<string, boolean | string> = Map({ isFetching: true });
const config = (state = defaultState, action: ConfigAction) => { function config(state = defaultState, action: ConfigAction) {
switch (action.type) { switch (action.type) {
case CONFIG_REQUEST: case CONFIG_REQUEST:
return state.set('isFetching', true); return state.set('isFetching', true);
@ -24,11 +24,14 @@ const config = (state = defaultState, action: ConfigAction) => {
default: default:
return state; return state;
} }
}; }
export const selectLocale = (state: Config) => state.get('locale', 'en') as string; export function selectLocale(state: Config) {
return state.get('locale', 'en') as string;
}
export const selectUseWorkflow = (state: Config) => export function selectUseWorkflow(state: Config) {
state.get('publish_mode') === EDITORIAL_WORKFLOW; return state.get('publish_mode') === EDITORIAL_WORKFLOW;
}
export default config; export default config;

View File

@ -10,10 +10,11 @@ import {
// Since pagination can be used for a variety of views (collections // Since pagination can be used for a variety of views (collections
// and searches are the most common examples), we namespace cursors by // and searches are the most common examples), we namespace cursors by
// their type before storing them in the state. // their type before storing them in the state.
export const selectCollectionEntriesCursor = (state, collectionName) => export function selectCollectionEntriesCursor(state, collectionName) {
new Cursor(state.getIn(['cursorsByType', 'collectionEntries', collectionName])); return new Cursor(state.getIn(['cursorsByType', 'collectionEntries', collectionName]));
}
const cursors = (state = fromJS({ cursorsByType: { collectionEntries: {} } }), action) => { function cursors(state = fromJS({ cursorsByType: { collectionEntries: {} } }), action) {
switch (action.type) { switch (action.type) {
case ENTRIES_SUCCESS: { case ENTRIES_SUCCESS: {
return state.setIn( return state.setIn(
@ -29,6 +30,6 @@ const cursors = (state = fromJS({ cursorsByType: { collectionEntries: {} } }), a
default: default:
return state; return state;
} }
}; }
export default cursors; export default cursors;

View File

@ -5,7 +5,7 @@ import {
DEPLOY_PREVIEW_FAILURE, DEPLOY_PREVIEW_FAILURE,
} from 'Actions/deploys'; } from 'Actions/deploys';
const deploys = (state = Map({ deploys: Map() }), action) => { function deploys(state = Map({ deploys: Map() }), action) {
switch (action.type) { switch (action.type) {
case DEPLOY_PREVIEW_REQUEST: { case DEPLOY_PREVIEW_REQUEST: {
const { collection, slug } = action.payload; const { collection, slug } = action.payload;
@ -37,9 +37,10 @@ const deploys = (state = Map({ deploys: Map() }), action) => {
default: default:
return state; return state;
} }
}; }
export const selectDeployPreview = (state, collection, slug) => export function selectDeployPreview(state, collection, slug) {
state.getIn(['deploys', `${collection}.${slug}`]); return state.getIn(['deploys', `${collection}.${slug}`]);
}
export default deploys; export default deploys;

View File

@ -21,7 +21,7 @@ import {
import { CONFIG_SUCCESS } from '../actions/config'; import { CONFIG_SUCCESS } from '../actions/config';
import { EditorialWorkflowAction, EditorialWorkflow, Entities } from '../types/redux'; import { EditorialWorkflowAction, EditorialWorkflow, Entities } from '../types/redux';
const unpublishedEntries = (state = Map(), action: EditorialWorkflowAction) => { function unpublishedEntries(state = Map(), action: EditorialWorkflowAction) {
switch (action.type) { switch (action.type) {
case CONFIG_SUCCESS: { case CONFIG_SUCCESS: {
const publishMode = action.payload && action.payload.get('publish_mode'); const publishMode = action.payload && action.payload.get('publish_mode');
@ -137,27 +137,25 @@ const unpublishedEntries = (state = Map(), action: EditorialWorkflowAction) => {
default: default:
return state; return state;
} }
}; }
export const selectUnpublishedEntry = ( export function selectUnpublishedEntry(state: EditorialWorkflow, collection: string, slug: string) {
state: EditorialWorkflow, return state && state.getIn(['entities', `${collection}.${slug}`]);
collection: string, }
slug: string,
) => state && state.getIn(['entities', `${collection}.${slug}`]);
export const selectUnpublishedEntriesByStatus = (state: EditorialWorkflow, status: string) => { export function selectUnpublishedEntriesByStatus(state: EditorialWorkflow, status: string) {
if (!state) return null; if (!state) return null;
const entities = state.get('entities') as Entities; const entities = state.get('entities') as Entities;
return entities.filter(entry => entry.get('status') === status).valueSeq(); return entities.filter(entry => entry.get('status') === status).valueSeq();
}; }
export const selectUnpublishedSlugs = (state: EditorialWorkflow, collection: string) => { export function selectUnpublishedSlugs(state: EditorialWorkflow, collection: string) {
if (!state.get('entities')) return null; if (!state.get('entities')) return null;
const entities = state.get('entities') as Entities; const entities = state.get('entities') as Entities;
return entities return entities
.filter((_v, k) => startsWith(k as string, `${collection}.`)) .filter((_v, k) => startsWith(k as string, `${collection}.`))
.map(entry => entry.get('slug')) .map(entry => entry.get('slug'))
.valueSeq(); .valueSeq();
}; }
export default unpublishedEntries; export default unpublishedEntries;

View File

@ -95,11 +95,11 @@ const loadSort = once(() => {
return Map() as Sort; return Map() as Sort;
}); });
const clearSort = () => { function clearSort() {
localStorage.removeItem(storageSortKey); localStorage.removeItem(storageSortKey);
}; }
const persistSort = (sort: Sort | undefined) => { function persistSort(sort: Sort | undefined) {
if (sort) { if (sort) {
const storageSort: StorageSort = {}; const storageSort: StorageSort = {};
sort.keySeq().forEach(key => { sort.keySeq().forEach(key => {
@ -117,7 +117,7 @@ const persistSort = (sort: Sort | undefined) => {
} else { } else {
clearSort(); clearSort();
} }
}; }
const loadViewStyle = once(() => { const loadViewStyle = once(() => {
const viewStyle = localStorage.getItem(viewStyleKey); const viewStyle = localStorage.getItem(viewStyleKey);
@ -129,22 +129,22 @@ const loadViewStyle = once(() => {
return VIEW_STYLE_LIST; return VIEW_STYLE_LIST;
}); });
const clearViewStyle = () => { function clearViewStyle() {
localStorage.removeItem(viewStyleKey); localStorage.removeItem(viewStyleKey);
}; }
const persistViewStyle = (viewStyle: string | undefined) => { function persistViewStyle(viewStyle: string | undefined) {
if (viewStyle) { if (viewStyle) {
localStorage.setItem(viewStyleKey, viewStyle); localStorage.setItem(viewStyleKey, viewStyle);
} else { } else {
clearViewStyle(); clearViewStyle();
} }
}; }
const entries = ( function entries(
state = Map({ entities: Map(), pages: Map(), sort: loadSort(), viewStyle: loadViewStyle() }), state = Map({ entities: Map(), pages: Map(), sort: loadSort(), viewStyle: loadViewStyle() }),
action: EntriesAction, action: EntriesAction,
) => { ) {
switch (action.type) { switch (action.type) {
case ENTRY_REQUEST: { case ENTRY_REQUEST: {
const payload = action.payload as EntryRequestPayload; const payload = action.payload as EntryRequestPayload;
@ -344,30 +344,30 @@ const entries = (
default: default:
return state; return state;
} }
}; }
export const selectEntriesSort = (entries: Entries, collection: string) => { export function selectEntriesSort(entries: Entries, collection: string) {
const sort = entries.get('sort') as Sort | undefined; const sort = entries.get('sort') as Sort | undefined;
return sort?.get(collection); return sort?.get(collection);
}; }
export const selectEntriesFilter = (entries: Entries, collection: string) => { export function selectEntriesFilter(entries: Entries, collection: string) {
const filter = entries.get('filter') as Filter | undefined; const filter = entries.get('filter') as Filter | undefined;
return filter?.get(collection) || Map(); return filter?.get(collection) || Map();
}; }
export const selectEntriesGroup = (entries: Entries, collection: string) => { export function selectEntriesGroup(entries: Entries, collection: string) {
const group = entries.get('group') as Group | undefined; const group = entries.get('group') as Group | undefined;
return group?.get(collection) || Map(); return group?.get(collection) || Map();
}; }
export const selectEntriesGroupField = (entries: Entries, collection: string) => { export function selectEntriesGroupField(entries: Entries, collection: string) {
const groups = selectEntriesGroup(entries, collection); const groups = selectEntriesGroup(entries, collection);
const value = groups?.valueSeq().find(v => v?.get('active') === true); const value = groups?.valueSeq().find(v => v?.get('active') === true);
return value; return value;
}; }
export const selectEntriesSortFields = (entries: Entries, collection: string) => { export function selectEntriesSortFields(entries: Entries, collection: string) {
const sort = selectEntriesSort(entries, collection); const sort = selectEntriesSort(entries, collection);
const values = const values =
sort sort
@ -376,9 +376,9 @@ export const selectEntriesSortFields = (entries: Entries, collection: string) =>
.toArray() || []; .toArray() || [];
return values; return values;
}; }
export const selectEntriesFilterFields = (entries: Entries, collection: string) => { export function selectEntriesFilterFields(entries: Entries, collection: string) {
const filter = selectEntriesFilter(entries, collection); const filter = selectEntriesFilter(entries, collection);
const values = const values =
filter filter
@ -386,27 +386,29 @@ export const selectEntriesFilterFields = (entries: Entries, collection: string)
.filter(v => v?.get('active') === true) .filter(v => v?.get('active') === true)
.toArray() || []; .toArray() || [];
return values; return values;
}; }
export const selectViewStyle = (entries: Entries) => { export function selectViewStyle(entries: Entries) {
return entries.get('viewStyle'); return entries.get('viewStyle');
}; }
export const selectEntry = (state: Entries, collection: string, slug: string) => export function selectEntry(state: Entries, collection: string, slug: string) {
state.getIn(['entities', `${collection}.${slug}`]); return state.getIn(['entities', `${collection}.${slug}`]);
}
export const selectPublishedSlugs = (state: Entries, collection: string) => export function selectPublishedSlugs(state: Entries, collection: string) {
state.getIn(['pages', collection, 'ids'], List<string>()); return state.getIn(['pages', collection, 'ids'], List<string>());
}
const getPublishedEntries = (state: Entries, collectionName: string) => { function getPublishedEntries(state: Entries, collectionName: string) {
const slugs = selectPublishedSlugs(state, collectionName); const slugs = selectPublishedSlugs(state, collectionName);
const entries = const entries =
slugs && slugs &&
(slugs.map(slug => selectEntry(state, collectionName, slug as string)) as List<EntryMap>); (slugs.map(slug => selectEntry(state, collectionName, slug as string)) as List<EntryMap>);
return entries; return entries;
}; }
export const selectEntries = (state: Entries, collection: Collection) => { export function selectEntries(state: Entries, collection: Collection) {
const collectionName = collection.get('name'); const collectionName = collection.get('name');
let entries = getPublishedEntries(state, collectionName); let entries = getPublishedEntries(state, collectionName);
@ -438,9 +440,9 @@ export const selectEntries = (state: Entries, collection: Collection) => {
} }
return entries; return entries;
}; }
const getGroup = (entry: EntryMap, selectedGroup: GroupMap) => { function getGroup(entry: EntryMap, selectedGroup: GroupMap) {
const label = selectedGroup.get('label'); const label = selectedGroup.get('label');
const field = selectedGroup.get('field'); const field = selectedGroup.get('field');
@ -478,9 +480,9 @@ const getGroup = (entry: EntryMap, selectedGroup: GroupMap) => {
label, label,
value: typeof fieldData === 'boolean' ? fieldData : dataAsString, value: typeof fieldData === 'boolean' ? fieldData : dataAsString,
}; };
}; }
export const selectGroups = (state: Entries, collection: Collection) => { export function selectGroups(state: Entries, collection: Collection) {
const collectionName = collection.get('name'); const collectionName = collection.get('name');
const entries = getPublishedEntries(state, collectionName); const entries = getPublishedEntries(state, collectionName);
@ -507,37 +509,37 @@ export const selectGroups = (state: Entries, collection: Collection) => {
}); });
return groupsArray; return groupsArray;
}; }
export const selectEntryByPath = (state: Entries, collection: string, path: string) => { export function selectEntryByPath(state: Entries, collection: string, path: string) {
const slugs = selectPublishedSlugs(state, collection); const slugs = selectPublishedSlugs(state, collection);
const entries = const entries =
slugs && (slugs.map(slug => selectEntry(state, collection, slug as string)) as List<EntryMap>); slugs && (slugs.map(slug => selectEntry(state, collection, slug as string)) as List<EntryMap>);
return entries && entries.find(e => e?.get('path') === path); return entries && entries.find(e => e?.get('path') === path);
}; }
export const selectEntriesLoaded = (state: Entries, collection: string) => { export function selectEntriesLoaded(state: Entries, collection: string) {
return !!state.getIn(['pages', collection]); return !!state.getIn(['pages', collection]);
}; }
export const selectIsFetching = (state: Entries, collection: string) => { export function selectIsFetching(state: Entries, collection: string) {
return state.getIn(['pages', collection, 'isFetching'], false); return state.getIn(['pages', collection, 'isFetching'], false);
}; }
const DRAFT_MEDIA_FILES = 'DRAFT_MEDIA_FILES'; const DRAFT_MEDIA_FILES = 'DRAFT_MEDIA_FILES';
const getFileField = (collectionFiles: CollectionFiles, slug: string | undefined) => { function getFileField(collectionFiles: CollectionFiles, slug: string | undefined) {
const file = collectionFiles.find(f => f?.get('name') === slug); const file = collectionFiles.find(f => f?.get('name') === slug);
return file; return file;
}; }
const hasCustomFolder = ( function hasCustomFolder(
folderKey: 'media_folder' | 'public_folder', folderKey: 'media_folder' | 'public_folder',
collection: Collection | null, collection: Collection | null,
slug: string | undefined, slug: string | undefined,
field: EntryField | undefined, field: EntryField | undefined,
) => { ) {
if (!collection) { if (!collection) {
return false; return false;
} }
@ -558,9 +560,9 @@ const hasCustomFolder = (
} }
return false; return false;
}; }
const traverseFields = ( function traverseFields(
folderKey: 'media_folder' | 'public_folder', folderKey: 'media_folder' | 'public_folder',
config: Config, config: Config,
collection: Collection, collection: Collection,
@ -568,7 +570,7 @@ const traverseFields = (
field: EntryField, field: EntryField,
fields: EntryField[], fields: EntryField[],
currentFolder: string, currentFolder: string,
): string | null => { ): string | null {
const matchedField = fields.filter(f => f === field)[0]; const matchedField = fields.filter(f => f === field)[0];
if (matchedField) { if (matchedField) {
return folderFormatter( return folderFormatter(
@ -632,15 +634,15 @@ const traverseFields = (
} }
return null; return null;
}; }
const evaluateFolder = ( function evaluateFolder(
folderKey: 'media_folder' | 'public_folder', folderKey: 'media_folder' | 'public_folder',
config: Config, config: Config,
collection: Collection, collection: Collection,
entryMap: EntryMap | undefined, entryMap: EntryMap | undefined,
field: EntryField | undefined, field: EntryField | undefined,
) => { ) {
let currentFolder = config.get(folderKey); let currentFolder = config.get(folderKey);
// add identity template if doesn't exist // add identity template if doesn't exist
@ -723,14 +725,14 @@ const evaluateFolder = (
} }
return currentFolder; return currentFolder;
}; }
export const selectMediaFolder = ( export function selectMediaFolder(
config: Config, config: Config,
collection: Collection | null, collection: Collection | null,
entryMap: EntryMap | undefined, entryMap: EntryMap | undefined,
field: EntryField | undefined, field: EntryField | undefined,
) => { ) {
const name = 'media_folder'; const name = 'media_folder';
let mediaFolder = config.get(name); let mediaFolder = config.get(name);
@ -750,15 +752,15 @@ export const selectMediaFolder = (
} }
return trim(mediaFolder, '/'); return trim(mediaFolder, '/');
}; }
export const selectMediaFilePath = ( export function selectMediaFilePath(
config: Config, config: Config,
collection: Collection | null, collection: Collection | null,
entryMap: EntryMap | undefined, entryMap: EntryMap | undefined,
mediaPath: string, mediaPath: string,
field: EntryField | undefined, field: EntryField | undefined,
) => { ) {
if (isAbsolutePath(mediaPath)) { if (isAbsolutePath(mediaPath)) {
return mediaPath; return mediaPath;
} }
@ -766,15 +768,15 @@ export const selectMediaFilePath = (
const mediaFolder = selectMediaFolder(config, collection, entryMap, field); const mediaFolder = selectMediaFolder(config, collection, entryMap, field);
return join(mediaFolder, basename(mediaPath)); return join(mediaFolder, basename(mediaPath));
}; }
export const selectMediaFilePublicPath = ( export function selectMediaFilePublicPath(
config: Config, config: Config,
collection: Collection | null, collection: Collection | null,
mediaPath: string, mediaPath: string,
entryMap: EntryMap | undefined, entryMap: EntryMap | undefined,
field: EntryField | undefined, field: EntryField | undefined,
) => { ) {
if (isAbsolutePath(mediaPath)) { if (isAbsolutePath(mediaPath)) {
return mediaPath; return mediaPath;
} }
@ -789,12 +791,12 @@ export const selectMediaFilePublicPath = (
} }
return join(publicFolder, basename(mediaPath)); return join(publicFolder, basename(mediaPath));
}; }
export const selectEditingDraft = (state: EntryDraft) => { export function selectEditingDraft(state: EntryDraft) {
const entry = state.get('entry'); const entry = state.get('entry');
const workflowDraft = entry && !entry.isEmpty(); const workflowDraft = entry && !entry.isEmpty();
return workflowDraft; return workflowDraft;
}; }
export default entries; export default entries;

View File

@ -35,7 +35,7 @@ const initialState = Map({
key: '', key: '',
}); });
const entryDraftReducer = (state = Map(), action) => { function entryDraftReducer(state = Map(), action) {
switch (action.type) { switch (action.type) {
case DRAFT_CREATE_FROM_ENTRY: case DRAFT_CREATE_FROM_ENTRY:
// Existing Entry // Existing Entry
@ -180,9 +180,9 @@ const entryDraftReducer = (state = Map(), action) => {
default: default:
return state; return state;
} }
}; }
export const selectCustomPath = (collection, entryDraft) => { export function selectCustomPath(collection, entryDraft) {
if (!selectHasMetaPath(collection)) { if (!selectHasMetaPath(collection)) {
return; return;
} }
@ -192,6 +192,6 @@ export const selectCustomPath = (collection, entryDraft) => {
const extension = selectFolderEntryExtension(collection); const extension = selectFolderEntryExtension(collection);
const customPath = path && join(collection.get('folder'), path, `${indexFile}.${extension}`); const customPath = path && join(collection.get('folder'), path, `${indexFile}.${extension}`);
return customPath; return customPath;
}; }
export default entryDraftReducer; export default entryDraftReducer;

View File

@ -8,12 +8,14 @@ const LOADING_IGNORE_LIST = [
'STATUS_FAILURE', 'STATUS_FAILURE',
]; ];
const ignoreWhenLoading = action => LOADING_IGNORE_LIST.some(type => action.type.includes(type)); function ignoreWhenLoading(action) {
return LOADING_IGNORE_LIST.some(type => action.type.includes(type));
}
/* /**
* Reducer for some global UI state that we want to share between components * Reducer for some global UI state that we want to share between components
* */ */
const globalUI = (state = Map({ isFetching: false, useOpenAuthoring: false }), action) => { function globalUI(state = Map({ isFetching: false, useOpenAuthoring: false }), action) {
// Generic, global loading indicator // Generic, global loading indicator
if (!ignoreWhenLoading(action) && action.type.includes('REQUEST')) { if (!ignoreWhenLoading(action) && action.type.includes('REQUEST')) {
return state.set('isFetching', true); return state.set('isFetching', true);
@ -26,6 +28,6 @@ const globalUI = (state = Map({ isFetching: false, useOpenAuthoring: false }), a
return state.set('useOpenAuthoring', true); return state.set('useOpenAuthoring', true);
} }
return state; return state;
}; }
export default globalUI; export default globalUI;

View File

@ -37,16 +37,19 @@ export default reducers;
/* /*
* Selectors * Selectors
*/ */
export const selectEntry = (state: State, collection: string, slug: string) => export function selectEntry(state: State, collection: string, slug: string) {
fromEntries.selectEntry(state.entries, collection, slug); return fromEntries.selectEntry(state.entries, collection, slug);
}
export const selectEntries = (state: State, collection: Collection) => export function selectEntries(state: State, collection: Collection) {
fromEntries.selectEntries(state.entries, collection); return fromEntries.selectEntries(state.entries, collection);
}
export const selectPublishedSlugs = (state: State, collection: string) => export function selectPublishedSlugs(state: State, collection: string) {
fromEntries.selectPublishedSlugs(state.entries, collection); return fromEntries.selectPublishedSlugs(state.entries, collection);
}
export const selectSearchedEntries = (state: State, availableCollections: string[]) => { export function selectSearchedEntries(state: State, availableCollections: string[]) {
const searchItems = state.search.get('entryIds'); const searchItems = state.search.get('entryIds');
// only return search results for actually available collections // only return search results for actually available collections
return ( return (
@ -55,19 +58,24 @@ export const selectSearchedEntries = (state: State, availableCollections: string
.filter(({ collection }) => availableCollections.indexOf(collection) !== -1) .filter(({ collection }) => availableCollections.indexOf(collection) !== -1)
.map(({ collection, slug }) => fromEntries.selectEntry(state.entries, collection, slug)) .map(({ collection, slug }) => fromEntries.selectEntry(state.entries, collection, slug))
); );
}; }
export const selectDeployPreview = (state: State, collection: string, slug: string) => export function selectDeployPreview(state: State, collection: string, slug: string) {
fromDeploys.selectDeployPreview(state.deploys, collection, slug); return fromDeploys.selectDeployPreview(state.deploys, collection, slug);
}
export const selectUnpublishedEntry = (state: State, collection: string, slug: string) => export function selectUnpublishedEntry(state: State, collection: string, slug: string) {
fromEditorialWorkflow.selectUnpublishedEntry(state.editorialWorkflow, collection, slug); return fromEditorialWorkflow.selectUnpublishedEntry(state.editorialWorkflow, collection, slug);
}
export const selectUnpublishedEntriesByStatus = (state: State, status: Status) => export function selectUnpublishedEntriesByStatus(state: State, status: Status) {
fromEditorialWorkflow.selectUnpublishedEntriesByStatus(state.editorialWorkflow, status); return fromEditorialWorkflow.selectUnpublishedEntriesByStatus(state.editorialWorkflow, status);
}
export const selectUnpublishedSlugs = (state: State, collection: string) => export function selectUnpublishedSlugs(state: State, collection: string) {
fromEditorialWorkflow.selectUnpublishedSlugs(state.editorialWorkflow, collection); return fromEditorialWorkflow.selectUnpublishedSlugs(state.editorialWorkflow, collection);
}
export const selectIntegration = (state: State, collection: string | null, hook: string) => export function selectIntegration(state: State, collection: string | null, hook: string) {
fromIntegrations.selectIntegration(state.integrations, collection, hook); return fromIntegrations.selectIntegration(state.integrations, collection, hook);
}

View File

@ -7,7 +7,7 @@ interface Acc {
hooks: Record<string, string | Record<string, string>>; hooks: Record<string, string | Record<string, string>>;
} }
export const getIntegrations = (config: Config) => { export function getIntegrations(config: Config) {
const integrations: Integration[] = config.get('integrations', List()).toJS() || []; const integrations: Integration[] = config.get('integrations', List()).toJS() || [];
const newState = integrations.reduce( const newState = integrations.reduce(
(acc, integration) => { (acc, integration) => {
@ -38,9 +38,9 @@ export const getIntegrations = (config: Config) => {
{ providers: {}, hooks: {} } as Acc, { providers: {}, hooks: {} } as Acc,
); );
return fromJS(newState); return fromJS(newState);
}; }
const integrations = (state = null, action: IntegrationsAction): Integrations | null => { function integrations(state = null, action: IntegrationsAction): Integrations | null {
switch (action.type) { switch (action.type) {
case CONFIG_SUCCESS: { case CONFIG_SUCCESS: {
return getIntegrations(action.payload); return getIntegrations(action.payload);
@ -48,11 +48,12 @@ const integrations = (state = null, action: IntegrationsAction): Integrations |
default: default:
return state; return state;
} }
}; }
export const selectIntegration = (state: Integrations, collection: string | null, hook: string) => export function selectIntegration(state: Integrations, collection: string | null, hook: string) {
collection return collection
? state.getIn(['hooks', collection, hook], false) ? state.getIn(['hooks', collection, hook], false)
: state.getIn(['hooks', hook], false); : state.getIn(['hooks', hook], false);
}
export default integrations; export default integrations;

View File

@ -51,7 +51,7 @@ const defaultState: {
config: Map(), config: Map(),
}; };
const mediaLibrary = (state = Map(defaultState), action: MediaLibraryAction) => { function mediaLibrary(state = Map(defaultState), action: MediaLibraryAction) {
switch (action.type) { switch (action.type) {
case MEDIA_LIBRARY_CREATE: case MEDIA_LIBRARY_CREATE:
return state.withMutations(map => { return state.withMutations(map => {
@ -213,7 +213,7 @@ const mediaLibrary = (state = Map(defaultState), action: MediaLibraryAction) =>
default: default:
return state; return state;
} }
}; }
export function selectMediaFiles(state: State, field?: EntryField) { export function selectMediaFiles(state: State, field?: EntryField) {
const { mediaLibrary, entryDraft } = state; const { mediaLibrary, entryDraft } = state;

View File

@ -57,7 +57,8 @@ const medias = produce((state: Medias, action: MediasAction) => {
} }
}, defaultState); }, defaultState);
export const selectIsLoadingAsset = (state: Medias) => export function selectIsLoadingAsset(state: Medias) {
Object.values(state).some(state => state.isLoading); return Object.values(state).some(state => state.isLoading);
}
export default medias; export default medias;

View File

@ -23,7 +23,7 @@ const defaultState = Map({
queryHits: Map({}), queryHits: Map({}),
}); });
const entries = (state = defaultState, action) => { function entries(state = defaultState, action) {
switch (action.type) { switch (action.type) {
case SEARCH_CLEAR: case SEARCH_CLEAR:
return defaultState; return defaultState;
@ -84,6 +84,6 @@ const entries = (state = defaultState, action) => {
default: default:
return state; return state;
} }
}; }
export default entries; export default entries;

View File

@ -21,6 +21,7 @@ interface WaitAction extends WaitActionArgs {
type: typeof WAIT_UNTIL_ACTION; type: typeof WAIT_UNTIL_ACTION;
} }
// eslint-disable-next-line func-style
export const waitUntilAction: Middleware<{}, State, Dispatch> = ({ export const waitUntilAction: Middleware<{}, State, Dispatch> = ({
dispatch, dispatch,
getState, getState,
@ -50,7 +51,7 @@ export const waitUntilAction: Middleware<{}, State, Dispatch> = ({
} }
} }
return (next: Dispatch) => (action: AnyAction): null | AnyAction => { return (next: Dispatch<AnyAction>) => (action: AnyAction): null | AnyAction => {
if (action.type === WAIT_UNTIL_ACTION) { if (action.type === WAIT_UNTIL_ACTION) {
pending.push(action as WaitAction); pending.push(action as WaitAction);
return null; return null;

View File

@ -2,11 +2,16 @@ import { createHashHistory } from 'history';
const history = createHashHistory(); const history = createHashHistory();
export const navigateToCollection = (collectionName: string) => export function navigateToCollection(collectionName: string) {
history.push(`/collections/${collectionName}`); return history.push(`/collections/${collectionName}`);
export const navigateToNewEntry = (collectionName: string) => }
history.replace(`/collections/${collectionName}/new`);
export const navigateToEntry = (collectionName: string, slug: string) => export function navigateToNewEntry(collectionName: string) {
history.replace(`/collections/${collectionName}/entries/${slug}`); return history.replace(`/collections/${collectionName}/new`);
}
export function navigateToEntry(collectionName: string, slug: string) {
return history.replace(`/collections/${collectionName}/entries/${slug}`);
}
export default history; export default history;

View File

@ -2,7 +2,10 @@ import { fromJS } from 'immutable';
import { isFunction } from 'lodash'; import { isFunction } from 'lodash';
const catchesNothing = /.^/; const catchesNothing = /.^/;
const bind = fn => isFunction(fn) && fn.bind(null);
function bind(fn) {
return isFunction(fn) && fn.bind(null);
}
export default function createEditorComponent(config) { export default function createEditorComponent(config) {
const { const {

View File

@ -8,13 +8,13 @@ const versionPlugin = new webpack.DefinePlugin({
NETLIFY_CMS_CORE_VERSION: JSON.stringify(`${pkg.version}${isProduction ? '' : '-dev'}`), NETLIFY_CMS_CORE_VERSION: JSON.stringify(`${pkg.version}${isProduction ? '' : '-dev'}`),
}); });
const configs = () => { function configs() {
return getConfig().map(config => { return getConfig().map(config => {
return { return {
...config, ...config,
plugins: [...config.plugins, versionPlugin], plugins: [...config.plugins, versionPlugin],
}; };
}); });
}; }
module.exports = configs(); module.exports = configs();

View File

@ -1,6 +1,9 @@
import component from '../index'; import component from '../index';
const getAsset = path => path; function getAsset(path) {
return path;
}
const image = '/image'; const image = '/image';
const alt = 'alt'; const alt = 'alt';
const title = 'title'; const title = 'title';

View File

@ -38,11 +38,11 @@ class RateLimitError extends Error {
} }
} }
export const requestWithBackoff = async ( export async function requestWithBackoff(
api: API, api: API,
req: ApiRequest, req: ApiRequest,
attempt = 1, attempt = 1,
): Promise<Response> => { ): Promise<Response> {
if (api.rateLimiter) { if (api.rateLimiter) {
await api.rateLimiter.acquire(); await api.rateLimiter.acquire();
} }
@ -92,14 +92,14 @@ export const requestWithBackoff = async (
return requestWithBackoff(api, req, attempt + 1); return requestWithBackoff(api, req, attempt + 1);
} }
} }
}; }
export const readFile = async ( export async function readFile(
id: string | null | undefined, id: string | null | undefined,
fetchContent: () => Promise<string | Blob>, fetchContent: () => Promise<string | Blob>,
localForage: LocalForage, localForage: LocalForage,
isText: boolean, isText: boolean,
) => { ) {
const key = id ? (isText ? `gh.${id}` : `gh.${id}.blob`) : null; const key = id ? (isText ? `gh.${id}` : `gh.${id}.blob`) : null;
const cached = key ? await localForage.getItem<string | Blob>(key) : null; const cached = key ? await localForage.getItem<string | Blob>(key) : null;
if (cached) { if (cached) {
@ -111,20 +111,22 @@ export const readFile = async (
await localForage.setItem(key, content); await localForage.setItem(key, content);
} }
return content; return content;
}; }
export type FileMetadata = { export type FileMetadata = {
author: string; author: string;
updatedOn: string; updatedOn: string;
}; };
const getFileMetadataKey = (id: string) => `gh.${id}.meta`; function getFileMetadataKey(id: string) {
return `gh.${id}.meta`;
}
export const readFileMetadata = async ( export async function readFileMetadata(
id: string | null | undefined, id: string | null | undefined,
fetchMetadata: () => Promise<FileMetadata>, fetchMetadata: () => Promise<FileMetadata>,
localForage: LocalForage, localForage: LocalForage,
) => { ) {
const key = id ? getFileMetadataKey(id) : null; const key = id ? getFileMetadataKey(id) : null;
const cached = key && (await localForage.getItem<FileMetadata>(key)); const cached = key && (await localForage.getItem<FileMetadata>(key));
if (cached) { if (cached) {
@ -136,7 +138,7 @@ export const readFileMetadata = async (
await localForage.setItem<FileMetadata>(key, metadata); await localForage.setItem<FileMetadata>(key, metadata);
} }
return metadata; return metadata;
}; }
/** /**
* Keywords for inferring a status that will provide a deploy preview URL. * Keywords for inferring a status that will provide a deploy preview URL.
@ -148,12 +150,12 @@ const PREVIEW_CONTEXT_KEYWORDS = ['deploy'];
* deploy preview. Checks for an exact match against `previewContext` if given, * deploy preview. Checks for an exact match against `previewContext` if given,
* otherwise checks for inclusion of a value from `PREVIEW_CONTEXT_KEYWORDS`. * otherwise checks for inclusion of a value from `PREVIEW_CONTEXT_KEYWORDS`.
*/ */
export const isPreviewContext = (context: string, previewContext: string) => { export function isPreviewContext(context: string, previewContext: string) {
if (previewContext) { if (previewContext) {
return context === previewContext; return context === previewContext;
} }
return PREVIEW_CONTEXT_KEYWORDS.some(keyword => context.includes(keyword)); return PREVIEW_CONTEXT_KEYWORDS.some(keyword => context.includes(keyword));
}; }
export enum PreviewState { export enum PreviewState {
Other = 'other', Other = 'other',
@ -164,20 +166,20 @@ export enum PreviewState {
* Retrieve a deploy preview URL from an array of statuses. By default, a * Retrieve a deploy preview URL from an array of statuses. By default, a
* matching status is inferred via `isPreviewContext`. * matching status is inferred via `isPreviewContext`.
*/ */
export const getPreviewStatus = ( export function getPreviewStatus(
statuses: { statuses: {
context: string; context: string;
target_url: string; target_url: string;
state: PreviewState; state: PreviewState;
}[], }[],
previewContext: string, previewContext: string,
) => { ) {
return statuses.find(({ context }) => { return statuses.find(({ context }) => {
return isPreviewContext(context, previewContext); return isPreviewContext(context, previewContext);
}); });
}; }
const getConflictingBranches = (branchName: string) => { function getConflictingBranches(branchName: string) {
// for cms/posts/post-1, conflicting branches are cms/posts, cms // for cms/posts/post-1, conflicting branches are cms/posts, cms
const parts = branchName.split('/'); const parts = branchName.split('/');
parts.pop(); parts.pop();
@ -188,13 +190,13 @@ const getConflictingBranches = (branchName: string) => {
}, [] as string[]); }, [] as string[]);
return conflictingBranches; return conflictingBranches;
}; }
export const throwOnConflictingBranches = async ( export async function throwOnConflictingBranches(
branchName: string, branchName: string,
getBranch: (name: string) => Promise<{ name: string }>, getBranch: (name: string) => Promise<{ name: string }>,
apiName: string, apiName: string,
) => { ) {
const possibleConflictingBranches = getConflictingBranches(branchName); const possibleConflictingBranches = getConflictingBranches(branchName);
const conflictingBranches = await Promise.all( const conflictingBranches = await Promise.all(
@ -213,4 +215,4 @@ export const throwOnConflictingBranches = async (
apiName, apiName,
); );
} }
}; }

View File

@ -3,27 +3,36 @@ export const DEFAULT_PR_BODY = 'Automatically generated by Netlify CMS';
export const MERGE_COMMIT_MESSAGE = 'Automatically generated. Merged on Netlify CMS.'; export const MERGE_COMMIT_MESSAGE = 'Automatically generated. Merged on Netlify CMS.';
const DEFAULT_NETLIFY_CMS_LABEL_PREFIX = 'netlify-cms/'; const DEFAULT_NETLIFY_CMS_LABEL_PREFIX = 'netlify-cms/';
const getLabelPrefix = (labelPrefix: string) => labelPrefix || DEFAULT_NETLIFY_CMS_LABEL_PREFIX;
export const isCMSLabel = (label: string, labelPrefix: string) => function getLabelPrefix(labelPrefix: string) {
label.startsWith(getLabelPrefix(labelPrefix)); return labelPrefix || DEFAULT_NETLIFY_CMS_LABEL_PREFIX;
export const labelToStatus = (label: string, labelPrefix: string) => }
label.substr(getLabelPrefix(labelPrefix).length);
export const statusToLabel = (status: string, labelPrefix: string) =>
`${getLabelPrefix(labelPrefix)}${status}`;
export const generateContentKey = (collectionName: string, slug: string) => export function isCMSLabel(label: string, labelPrefix: string) {
`${collectionName}/${slug}`; return label.startsWith(getLabelPrefix(labelPrefix));
}
export const parseContentKey = (contentKey: string) => { export function labelToStatus(label: string, labelPrefix: string) {
return label.substr(getLabelPrefix(labelPrefix).length);
}
export function statusToLabel(status: string, labelPrefix: string) {
return `${getLabelPrefix(labelPrefix)}${status}`;
}
export function generateContentKey(collectionName: string, slug: string) {
return `${collectionName}/${slug}`;
}
export function parseContentKey(contentKey: string) {
const index = contentKey.indexOf('/'); const index = contentKey.indexOf('/');
return { collection: contentKey.substr(0, index), slug: contentKey.substr(index + 1) }; return { collection: contentKey.substr(0, index), slug: contentKey.substr(index + 1) };
}; }
export const contentKeyFromBranch = (branch: string) => { export function contentKeyFromBranch(branch: string) {
return branch.substring(`${CMS_BRANCH_PREFIX}/`.length); return branch.substring(`${CMS_BRANCH_PREFIX}/`.length);
}; }
export const branchFromContentKey = (contentKey: string) => { export function branchFromContentKey(contentKey: string) {
return `${CMS_BRANCH_PREFIX}/${contentKey}`; return `${CMS_BRANCH_PREFIX}/${contentKey}`;
}; }

View File

@ -27,7 +27,7 @@ export type CursorStore = {
type ActionHandler = (action: string) => unknown; type ActionHandler = (action: string) => unknown;
const jsToMap = (obj: {}) => { function jsToMap(obj: {}) {
if (obj === undefined) { if (obj === undefined) {
return Map(); return Map();
} }
@ -36,7 +36,7 @@ const jsToMap = (obj: {}) => {
throw new Error('Object must be equivalent to a Map.'); throw new Error('Object must be equivalent to a Map.');
} }
return immutableObj; return immutableObj;
}; }
const knownMetaKeys = Set([ const knownMetaKeys = Set([
'index', 'index',
@ -49,8 +49,10 @@ const knownMetaKeys = Set([
'folder', 'folder',
'depth', 'depth',
]); ]);
const filterUnknownMetaKeys = (meta: Map<string, string>) =>
meta.filter((_v, k) => knownMetaKeys.has(k as string)); function filterUnknownMetaKeys(meta: Map<string, string>) {
return meta.filter((_v, k) => knownMetaKeys.has(k as string));
}
/* /*
createCursorMap takes one of three signatures: createCursorMap takes one of three signatures:
@ -58,7 +60,7 @@ const filterUnknownMetaKeys = (meta: Map<string, string>) =>
- (cursorMap: <object/Map with optional actions, data, and meta keys>) -> cursor - (cursorMap: <object/Map with optional actions, data, and meta keys>) -> cursor
- (actions: <array/List>, data: <object/Map>, meta: <optional object/Map>) -> cursor - (actions: <array/List>, data: <object/Map>, meta: <optional object/Map>) -> cursor
*/ */
const createCursorStore = (...args: {}[]) => { function createCursorStore(...args: {}[]) {
const { actions, data, meta } = const { actions, data, meta } =
args.length === 1 args.length === 1
? jsToMap(args[0]).toObject() ? jsToMap(args[0]).toObject()
@ -71,15 +73,18 @@ const createCursorStore = (...args: {}[]) => {
data: jsToMap(data), data: jsToMap(data),
meta: jsToMap(meta).update(filterUnknownMetaKeys), meta: jsToMap(meta).update(filterUnknownMetaKeys),
}) as CursorStore; }) as CursorStore;
}; }
const hasAction = (store: CursorStore, action: string) => store.hasIn(['actions', action]); function hasAction(store: CursorStore, action: string) {
return store.hasIn(['actions', action]);
}
const getActionHandlers = (store: CursorStore, handler: ActionHandler) => function getActionHandlers(store: CursorStore, handler: ActionHandler) {
store return store
.get('actions', Set<string>()) .get('actions', Set<string>())
.toMap() .toMap()
.map(action => handler(action as string)); .map(action => handler(action as string));
}
// The cursor logic is entirely functional, so this class simply // The cursor logic is entirely functional, so this class simply
// provides a chainable interface // provides a chainable interface

View File

@ -21,17 +21,20 @@ describe('parseLinkHeader', () => {
}); });
describe('getAllResponses', () => { describe('getAllResponses', () => {
const generatePulls = length => { function generatePulls(length) {
return Array.from({ length }, (_, id) => { return Array.from({ length }, (_, id) => {
return { id: id + 1, number: `134${id}`, state: 'open' }; return { id: id + 1, number: `134${id}`, state: 'open' };
}); });
}; }
function createLinkHeaders({ page, pageCount }) { function createLinkHeaders({ page, pageCount }) {
const pageNum = parseInt(page, 10); const pageNum = parseInt(page, 10);
const pageCountNum = parseInt(pageCount, 10); const pageCountNum = parseInt(pageCount, 10);
const url = 'https://api.github.com/pulls'; const url = 'https://api.github.com/pulls';
const link = linkPage => `<${url}?page=${linkPage}>`;
function link(linkPage) {
return `<${url}?page=${linkPage}>`;
}
const linkHeader = oneLine` const linkHeader = oneLine`
${pageNum === 1 ? '' : `${link(1)}; rel="first",`} ${pageNum === 1 ? '' : `${link(1)}; rel="first",`}

View File

@ -2,10 +2,10 @@ import semaphore from 'semaphore';
export type AsyncLock = { release: () => void; acquire: () => Promise<boolean> }; export type AsyncLock = { release: () => void; acquire: () => Promise<boolean> };
export const asyncLock = (): AsyncLock => { export function asyncLock(): AsyncLock {
let lock = semaphore(1); let lock = semaphore(1);
const acquire = (timeout = 15000) => { function acquire(timeout = 15000) {
const promise = new Promise<boolean>(resolve => { const promise = new Promise<boolean>(resolve => {
// this makes sure a caller doesn't gets stuck forever awaiting on the lock // this makes sure a caller doesn't gets stuck forever awaiting on the lock
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
@ -21,9 +21,9 @@ export const asyncLock = (): AsyncLock => {
}); });
return promise; return promise;
}; }
const release = () => { function release() {
try { try {
// suppress too many calls to leave error // suppress too many calls to leave error
lock.leave(); lock.leave();
@ -37,7 +37,7 @@ export const asyncLock = (): AsyncLock => {
lock = semaphore(1); lock = semaphore(1);
} }
} }
}; }
return { acquire, release }; return { acquire, release };
}; }

Some files were not shown because too many files have changed in this diff Show More