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',
'require-atomic-updates': [0],
'object-shorthand': ['error', 'always'],
'func-style': ['error', 'declaration'],
'prefer-const': [
'error',
{

View File

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

View File

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

View File

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

View File

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

View File

@ -121,11 +121,11 @@ interface NetlifyUser extends Credentials {
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 response = await fetch(`${apiRoot}/${path}`).then(res => res.json());
return response;
};
}
export default class GitGateway implements Implementation {
config: Config;

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ describe('github API', () => {
jest.resetAllMocks();
});
const mockAPI = (api, responses) => {
function mockAPI(api, responses) {
api.request = jest.fn().mockImplementation((path, options = {}) => {
const normalizedPath = path.indexOf('?') !== -1 ? path.substr(0, path.indexOf('?')) : path;
const response = responses[normalizedPath];
@ -16,7 +16,7 @@ describe('github API', () => {
? Promise.resolve(response(options))
: Promise.reject(new Error(`No response for path '${normalizedPath}'`));
});
};
}
describe('editorialWorkflowGit', () => {
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}
`;
const buildFilesQuery = (depth = 1) => {
function buildFilesQuery(depth = 1) {
const PLACE_HOLDER = 'PLACE_HOLDER';
let query = oneLine`
...ObjectParts
@ -93,9 +93,10 @@ const buildFilesQuery = (depth = 1) => {
query = query.replace(PLACE_HOLDER, '');
return query;
};
}
export const files = (depth: number) => gql`
export function files(depth: number) {
return gql`
query files($owner: String!, $name: String!, $expression: String!) {
repository(owner: $owner, name: $name) {
...RepositoryParts
@ -107,7 +108,8 @@ export const files = (depth: number) => gql`
${fragments.repository}
${fragments.object}
${fragments.fileEntry}
`;
`;
}
const branchQueryPart = `
branch: ref(qualifiedName: $qualifiedName) {

View File

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

View File

@ -8,7 +8,7 @@ import AuthenticationPage from '../AuthenticationPage';
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 count = idx + 1;
const id = `00${count}`.slice(-3);
@ -37,7 +37,7 @@ const generateEntries = (path, length) => {
{},
),
};
};
}
const manyEntries = generateEntries('many-entries', 500);
@ -222,8 +222,10 @@ describe('gitlab backend', () => {
const pageNum = parseInt(page, 10);
const pageCountNum = parseInt(pageCount, 10);
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`
${link(1)}; rel="first",

View File

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

View File

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

View File

@ -817,14 +817,14 @@ describe('config', () => {
});
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).toHaveBeenCalledWith(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'info' }),
});
};
}
beforeEach(() => {
delete window.location;

View File

@ -13,7 +13,7 @@ export const CONFIG_REQUEST = 'CONFIG_REQUEST';
export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
export const CONFIG_FAILURE = 'CONFIG_FAILURE';
const traverseFieldsJS = (fields, updater) => {
function traverseFieldsJS(fields, updater) {
return fields.map(field => {
let newField = updater(field);
if (newField.fields) {
@ -26,9 +26,9 @@ const traverseFieldsJS = (fields, updater) => {
return newField;
});
};
}
const getConfigUrl = () => {
function getConfigUrl() {
const validTypes = { 'text/yaml': 'yaml', 'application/x-yaml': 'yaml' };
const configLinkEl = document.querySelector('link[rel="cms-config-url"]');
const isValidLink = configLinkEl && validTypes[configLinkEl.type] && get(configLinkEl, 'href');
@ -38,14 +38,14 @@ const getConfigUrl = () => {
return link;
}
return 'config.yml';
};
}
const setDefaultPublicFolder = map => {
function setDefaultPublicFolder(map) {
if (map.has('media_folder') && !map.has('public_folder')) {
map = map.set('public_folder', map.get('media_folder'));
}
return map;
};
}
// Mapping between existing camelCase and its snake_case counterpart
const WIDGET_KEY_MAP = {
@ -60,7 +60,7 @@ const WIDGET_KEY_MAP = {
optionsLength: 'options_length',
};
const setSnakeCaseConfig = field => {
function setSnakeCaseConfig(field) {
const deprecatedKeys = Object.keys(WIDGET_KEY_MAP).filter(camel => camel in field);
const snakeValues = deprecatedKeys.map(camel => {
const snake = WIDGET_KEY_MAP[camel];
@ -71,18 +71,18 @@ const setSnakeCaseConfig = field => {
});
return Object.assign({}, field, ...snakeValues);
};
}
const setI18nField = field => {
function setI18nField(field) {
if (field.get(I18N) === true) {
field = field.set(I18N, I18N_FIELD.TRANSLATE);
} else if (field.get(I18N) === false || !field.has(I18N)) {
field = field.set(I18N, I18N_FIELD.NONE);
}
return field;
};
}
const setI18nDefaults = (defaultI18n, collectionOrFile) => {
function setI18nDefaults(defaultI18n, collectionOrFile) {
if (defaultI18n && collectionOrFile.has(I18N)) {
const collectionOrFileI18n = collectionOrFile.get(I18N);
if (collectionOrFileI18n === true) {
@ -121,17 +121,17 @@ const setI18nDefaults = (defaultI18n, collectionOrFile) => {
}
}
return collectionOrFile;
};
}
const throwOnInvalidFileCollectionStructure = i18n => {
function throwOnInvalidFileCollectionStructure(i18n) {
if (i18n && i18n.get('structure') !== I18N_STRUCTURE.SINGLE_FILE) {
throw new Error(
`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'))) {
throw new Error(
`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)) {
collection = collection.set(key, fromJS([]));
} else {
@ -152,17 +152,17 @@ const setViewPatternsDefaults = (key, collection) => {
}
return collection;
};
}
const defaults = {
publish_mode: SIMPLE_PUBLISH_MODE,
};
const hasIntegration = (config, collection) => {
function hasIntegration(config, collection) {
const integrations = getIntegrations(config);
const integration = selectIntegration(integrations, collection.get('name'), 'listEntries');
return !!integration;
};
}
export function normalizeConfig(config) {
const { collections = [] } = config;
@ -390,7 +390,7 @@ export async function detectProxyServer(localBackend) {
return {};
}
const getPublishMode = (config, publishModes, backendType) => {
function getPublishMode(config, publishModes, backendType) {
if (config.publish_mode && publishModes && !publishModes.includes(config.publish_mode)) {
const newPublishMode = publishModes[0];
console.log(
@ -400,9 +400,9 @@ const getPublishMode = (config, publishModes, backendType) => {
}
return config.publish_mode;
};
}
export const handleLocalBackend = async config => {
export async function handleLocalBackend(config) {
if (!config.local_backend) {
return config;
}
@ -421,7 +421,7 @@ export const handleLocalBackend = async config => {
...(publishMode && { publish_mode: publishMode }),
backend: { ...config.backend, name: 'proxy', proxy_url: proxyUrl },
};
};
}
export function loadConfig(manualConfig = {}, onLoad) {
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 integration = selectIntegration(state, collection.get('name'), 'listEntries');
const provider: Backend = integration
@ -162,7 +162,7 @@ const getAllEntries = async (state: State, collection: Collection) => {
: backend;
const entries = await provider.listAllEntries(collection);
return entries;
};
}
export function sortByField(
collection: Collection,
@ -555,7 +555,7 @@ const appendActions = fromJS({
['append_next']: { action: 'next', append: true },
});
const addAppendActionsToCursor = (cursor: Cursor) => {
function addAppendActionsToCursor(cursor: Cursor) {
return Cursor.create(cursor).updateStore('actions', (actions: Set<string>) => {
return actions.union(
appendActions
@ -563,7 +563,7 @@ const addAppendActionsToCursor = (cursor: Cursor) => {
.keySeq(),
);
});
};
}
export function loadEntries(collection: Collection, page = 0) {
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
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
};
}
const processValue = (unsafe: string) => {
function processValue(unsafe: string) {
if (['true', 'True', 'TRUE'].includes(unsafe)) {
return true;
}
@ -710,10 +710,15 @@ const processValue = (unsafe: string) => {
}
return escapeHtml(unsafe);
};
}
const getDataFields = (fields: EntryFields) => fields.filter(f => !f!.get('meta')).toList();
const getMetaFields = (fields: EntryFields) => fields.filter(f => f!.get('meta') === true).toList();
function getDataFields(fields: EntryFields) {
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) {
return async (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
@ -785,7 +790,10 @@ export function createEmptyDraftData(
const list = item.get('widget') == 'list';
const name = item.get('name');
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)) {
const subDefaultValue = list
@ -831,9 +839,9 @@ function createEmptyDraftI18nData(collection: Collection, dataFields: EntryField
return {};
}
const skipField = (field: EntryField) => {
function skipField(field: EntryField) {
return field.get(I18N) !== I18N_FIELD.DUPLICATE && field.get(I18N) !== I18N_FIELD.TRANSLATE;
};
}
const i18nData = createEmptyDraftData(dataFields, true, skipField);
return duplicateDefaultI18nFields(collection, i18nData);
@ -850,23 +858,25 @@ export function getMediaAssets({ entry }: { entry: EntryMap }) {
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
* update the entry and entryDraft with the serialized values.
*/
const fields = selectFields(collection, entry.get('slug'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const serializeData = (data: any) => {
function serializeData(data: any) {
return serializeValues(data, fields);
};
}
const serializedData = serializeData(entry.get('data'));
let serializedEntry = entry.set('data', serializedData);
if (hasI18n(collection)) {
serializedEntry = serializeI18n(collection, serializedEntry, serializeData);
}
return serializedEntry;
};
}
export function persistEntry(collection: Collection) {
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,
key: string,
t: (key: string, args: Record<string, unknown>) => string,
) => {
) {
return {
error: {
type: ValidationErrorTypes.CUSTOM,
@ -995,7 +1005,7 @@ const getPathError = (
}),
},
};
};
}
export function validateMetaField(
state: State,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ const CollectionTopDescription = styled.p`
margin-bottom: 0;
`;
const getCollectionProps = collection => {
function getCollectionProps(collection) {
const collectionLabel = collection.get('label');
const collectionLabelSingular = collection.get('label_singular');
const collectionDescription = collection.get('description');
@ -45,9 +45,9 @@ const getCollectionProps = collection => {
collectionLabelSingular,
collectionDescription,
};
};
}
const CollectionTop = ({ collection, newEntryUrl, t }) => {
function CollectionTop({ collection, newEntryUrl, t }) {
const { collectionLabel, collectionLabelSingular, collectionDescription } = getCollectionProps(
collection,
t,
@ -70,7 +70,7 @@ const CollectionTop = ({ collection, newEntryUrl, t }) => {
) : null}
</CollectionTopContainer>
);
};
}
CollectionTop.propTypes = {
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 (
<Button
css={css`
@ -24,4 +24,4 @@ export const ControlButton = ({ active, title }) => {
{title}
</Button>
);
};
}

View File

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

View File

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

View File

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

View File

@ -13,14 +13,15 @@ jest.mock('../Entries', () => 'mock-entries');
const middlewares = [];
const mockStore = configureStore(middlewares);
const renderWithRedux = (component, { store } = {}) => {
function renderWithRedux(component, { store } = {}) {
function Wrapper({ children }) {
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(
(acc, entry) => {
acc.entities[`${collection.get('name')}.${entry.slug}`] = entry;
@ -30,7 +31,7 @@ const toEntriesState = (collection, entriesArray) => {
{ pages: { [collection.get('name')]: { ids: [] } }, entities: {} },
);
return fromJS(entries);
};
}
describe('filterNestedEntries', () => {
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 { ControlButton } from './ControlButton';
const FilterControl = ({ viewFilters, t, onFilterClick, filter }) => {
function FilterControl({ viewFilters, t, onFilterClick, filter }) {
const hasActiveFilter = filter
?.valueSeq()
.toJS()
@ -33,6 +33,6 @@ const FilterControl = ({ viewFilters, t, onFilterClick, filter }) => {
})}
</Dropdown>
);
};
}
export default translate()(FilterControl);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,14 +5,19 @@ import { Map, List } from 'immutable';
import { oneLine } from 'common-tags';
import ValidationErrorTypes from 'Constants/validationErrorTypes';
const truthy = () => ({ error: false });
function truthy() {
return { error: false };
}
const isEmpty = value =>
function isEmpty(value) {
return (
value === null ||
value === undefined ||
(Object.prototype.hasOwnProperty.call(value, 'length') && 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 {
static propTypes = {

View File

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

View File

@ -248,24 +248,24 @@ PreviewPane.propTypes = {
getAsset: PropTypes.func.isRequired,
};
const mapStateToProps = state => {
function mapStateToProps(state) {
const isLoadingAsset = selectIsLoadingAsset(state.medias);
return { isLoadingAsset, config: state.config };
};
}
const mapDispatchToProps = dispatch => {
function mapDispatchToProps(dispatch) {
return {
boundGetAsset: (collection, entry) => boundGetAsset(dispatch, collection, entry),
};
};
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
function mergeProps(stateProps, dispatchProps, ownProps) {
return {
...stateProps,
...dispatchProps,
...ownProps,
getAsset: dispatchProps.boundGetAsset(ownProps.collection, ownProps.entry),
};
};
}
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 PropTypes from 'prop-types';
const UnknownControl = ({ field, t }) => {
function UnknownControl({ field, t }) {
return (
<div>{t('editor.editorWidgets.unknownControl.noControl', { widget: field.get('widget') })}</div>
);
};
}
UnknownControl.propTypes = {
field: ImmutablePropTypes.map,

View File

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

View File

@ -12,11 +12,13 @@ const EmptyMessageContainer = styled.div`
color: ${props => props.isPrivate && colors.textFieldBorder};
`;
const EmptyMessage = ({ content, isPrivate }) => (
function EmptyMessage({ content, isPrivate }) {
return (
<EmptyMessageContainer isPrivate={isPrivate}>
<h1>{content}</h1>
</EmptyMessageContainer>
);
);
}
EmptyMessage.propTypes = {
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 field = mediaLibrary.get('field');
const mediaLibraryProps = {
@ -377,7 +377,7 @@ const mapStateToProps = state => {
field,
};
return { ...mediaLibraryProps };
};
}
const mapDispatchToProps = {
loadMedia: loadMediaAction,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ import {
import React from 'react';
import PropTypes from 'prop-types';
export const DragSource = ({ namespace, ...props }) => {
export function DragSource({ namespace, ...props }) {
const DragComponent = ReactDNDDragSource(
namespace,
{
@ -23,13 +23,14 @@ export const DragSource = ({ namespace, ...props }) => {
)(({ children, connectDragComponent }) => children(connectDragComponent));
return React.createElement(DragComponent, props, props.children);
};
}
DragSource.propTypes = {
namespace: PropTypes.any.isRequired,
children: PropTypes.func.isRequired,
};
export const DropTarget = ({ onDrop, namespace, ...props }) => {
export function DropTarget({ onDrop, namespace, ...props }) {
const DropComponent = ReactDNDDropTarget(
namespace,
{
@ -44,11 +45,14 @@ export const DropTarget = ({ onDrop, namespace, ...props }) => {
)(({ children, connectDropTarget, isHovered }) => children(connectDropTarget, { isHovered }));
return React.createElement(DropComponent, props, props.children);
};
}
DropTarget.propTypes = {
onDrop: PropTypes.func.isRequired,
namespace: PropTypes.any.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';
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**
**To Reproduce**
@ -31,8 +33,9 @@ ${config}
**Additional context**
`;
}
const buildIssueTemplate = ({ config }) => {
function buildIssueTemplate({ config }) {
let version = '';
if (typeof NETLIFY_CMS_VERSION === 'string') {
version = `netlify-cms@${NETLIFY_CMS_VERSION}`;
@ -47,9 +50,9 @@ const buildIssueTemplate = ({ config }) => {
});
return template;
};
}
const buildIssueUrl = ({ title, config }) => {
function buildIssueUrl({ title, config }) {
try {
const body = buildIssueTemplate({ config });
@ -63,7 +66,7 @@ const buildIssueUrl = ({ title, config }) => {
console.log(e);
return `${ISSUE_URL}template=bug_report.md`;
}
};
}
const ErrorBoundaryContainer = styled.div`
padding: 40px;
@ -107,7 +110,7 @@ const CopyButton = styled.button`
margin: 12px 0;
`;
const RecoveredEntry = ({ entry, t }) => {
function RecoveredEntry({ entry, t }) {
console.log(entry);
return (
<>
@ -122,7 +125,7 @@ const RecoveredEntry = ({ entry, t }) => {
</pre>
</>
);
};
}
export class ErrorBoundary extends React.Component {
static propTypes = {

View File

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

View File

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

View File

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

View File

@ -6,7 +6,8 @@ import { translate } from 'react-polyglot';
import reduxNotificationsStyles from 'redux-notifications/lib/styles.css';
import { shadows, colors, lengths, zIndex } from 'netlify-cms-ui-default';
const ReduxNotificationsGlobalStyles = () => (
function ReduxNotificationsGlobalStyles() {
return (
<Global
styles={css`
${reduxNotificationsStyles};
@ -17,7 +18,8 @@ const ReduxNotificationsGlobalStyles = () => (
}
`}
/>
);
);
}
const styles = {
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]]}>
<ReduxNotificationsGlobalStyles />
{t(message.key, { details: message.details })}
</div>
);
);
}
Toast.propTypes = {
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 { oneLineTrim } from 'common-tags';
const WithError = () => {
function WithError() {
throw new Error('Some unknown error');
};
}
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) {
return 'lastChange';
} else if (date) {
@ -106,7 +106,7 @@ const lastChangePhraseKey = (date, author) => {
} else if (author) {
return 'lastChangeNoDate';
}
};
}
const CardDate = translate()(({ t, date, author }) => {
const key = lastChangePhraseKey(date, author);
@ -117,7 +117,7 @@ const CardDate = translate()(({ t, date, author }) => {
}
});
const WorkflowCard = ({
function WorkflowCard({
collectionLabel,
title,
authorLastChange,
@ -130,7 +130,8 @@ const WorkflowCard = ({
canPublish,
onPublish,
t,
}) => (
}) {
return (
<WorkflowCardContainer>
<WorkflowLink to={editLink}>
<CardCollection>{collectionLabel}</CardCollection>
@ -153,7 +154,8 @@ const WorkflowCard = ({
)}
</CardButtonContainer>
</WorkflowCardContainer>
);
);
}
WorkflowCard.propTypes = {
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
const DNDNamespace = 'cms-workflow';
const getColumnHeaderText = (columnName, t) => {
function getColumnHeaderText(columnName, t) {
switch (columnName) {
case 'draft':
return t('workflow.workflowList.draftHeader');
@ -125,7 +125,7 @@ const getColumnHeaderText = (columnName, t) => {
case 'pending_publish':
return t('workflow.workflowList.readyHeader');
}
};
}
class WorkflowList extends React.Component {
static propTypes = {

View File

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

View File

@ -28,8 +28,8 @@ export const extensionFormatters = {
html: FrontmatterInfer,
};
const formatByName = (name, customDelimiter) =>
({
function formatByName(name, customDelimiter) {
return {
yml: yamlFormatter,
yaml: yamlFormatter,
toml: tomlFormatter,
@ -38,7 +38,8 @@ const formatByName = (name, customDelimiter) =>
'json-frontmatter': frontmatterJSON(customDelimiter),
'toml-frontmatter': frontmatterTOML(customDelimiter),
'yaml-frontmatter': frontmatterYAML(customDelimiter),
}[name]);
}[name];
}
export function resolveFormat(collection, entry) {
// 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: '---' },
toml: { language: 'toml', delimiters: '+++' },
json: { language: 'json', delimiters: ['{', '}'] },
}[format]);
}[format];
}
class FrontmatterFormatter {
constructor(format, customDelimiter) {
@ -99,6 +100,15 @@ class FrontmatterFormatter {
}
export const FrontmatterInfer = new FrontmatterFormatter();
export const frontmatterYAML = customDelimiter => new FrontmatterFormatter('yaml', customDelimiter);
export const frontmatterTOML = customDelimiter => new FrontmatterFormatter('toml', customDelimiter);
export const frontmatterJSON = customDelimiter => new FrontmatterFormatter('json', customDelimiter);
export function frontmatterYAML(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 idxB = sortedKeys.indexOf(selector(b));
if (idxA === -1 || idxB === -1) return 0;
if (idxA > idxB) return 1;
if (idxA < idxB) return -1;
return 0;
};
};
}

View File

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

View File

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

View File

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

View File

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

View File

@ -20,7 +20,7 @@ import { getWidgetValueSerializer } from './registry';
* registered deserialization handlers run on entry load, and serialization
* 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
* are field values, serializing the values of fields whose widgets have
@ -63,12 +63,12 @@ const runSerializer = (values, fields, method) => {
serializedData = values.mergeDeep(serializedData);
return serializedData;
};
}
export const serializeValues = (values, fields) => {
export function serializeValues(values, fields) {
return runSerializer(values, fields, 'serialize');
};
}
export const deserializeValues = (values, fields) => {
export function deserializeValues(values, fields) {
return runSerializer(values, fields, 'deserialize');
};
}

View File

@ -36,8 +36,14 @@ export function stripProtocol(urlString: string) {
*/
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 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) {
let validChar: (char: string) => boolean;

View File

@ -18,6 +18,12 @@ interface MediaLibrary {
}) => 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 lib = (getMediaLibrary(name) as unknown) as MediaLibrary | undefined;
if (!lib) {
@ -26,9 +32,6 @@ const initializeMediaLibrary = once(async function initializeMediaLibrary(name,
);
store.dispatch(configFailed(err));
} 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 });
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, 'title', updater)).toEqual(

View File

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

View File

@ -3,12 +3,12 @@ import { connectRouter } from 'connected-react-router';
import { reducer as notifReducer } from 'redux-notifications';
import reducers from './index';
const createRootReducer = history => {
function createRootReducer(history) {
return combineReducers({
...reducers,
notifs: notifReducer,
router: connectRouter(history),
});
};
}
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 config = (state = defaultState, action: ConfigAction) => {
function config(state = defaultState, action: ConfigAction) {
switch (action.type) {
case CONFIG_REQUEST:
return state.set('isFetching', true);
@ -24,11 +24,14 @@ const config = (state = defaultState, action: ConfigAction) => {
default:
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) =>
state.get('publish_mode') === EDITORIAL_WORKFLOW;
export function selectUseWorkflow(state: Config) {
return state.get('publish_mode') === EDITORIAL_WORKFLOW;
}
export default config;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,12 +8,14 @@ const LOADING_IGNORE_LIST = [
'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
* */
const globalUI = (state = Map({ isFetching: false, useOpenAuthoring: false }), action) => {
*/
function globalUI(state = Map({ isFetching: false, useOpenAuthoring: false }), action) {
// Generic, global loading indicator
if (!ignoreWhenLoading(action) && action.type.includes('REQUEST')) {
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;
};
}
export default globalUI;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,11 +38,11 @@ class RateLimitError extends Error {
}
}
export const requestWithBackoff = async (
export async function requestWithBackoff(
api: API,
req: ApiRequest,
attempt = 1,
): Promise<Response> => {
): Promise<Response> {
if (api.rateLimiter) {
await api.rateLimiter.acquire();
}
@ -92,14 +92,14 @@ export const requestWithBackoff = async (
return requestWithBackoff(api, req, attempt + 1);
}
}
};
}
export const readFile = async (
export async function readFile(
id: string | null | undefined,
fetchContent: () => Promise<string | Blob>,
localForage: LocalForage,
isText: boolean,
) => {
) {
const key = id ? (isText ? `gh.${id}` : `gh.${id}.blob`) : null;
const cached = key ? await localForage.getItem<string | Blob>(key) : null;
if (cached) {
@ -111,20 +111,22 @@ export const readFile = async (
await localForage.setItem(key, content);
}
return content;
};
}
export type FileMetadata = {
author: 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,
fetchMetadata: () => Promise<FileMetadata>,
localForage: LocalForage,
) => {
) {
const key = id ? getFileMetadataKey(id) : null;
const cached = key && (await localForage.getItem<FileMetadata>(key));
if (cached) {
@ -136,7 +138,7 @@ export const readFileMetadata = async (
await localForage.setItem<FileMetadata>(key, metadata);
}
return metadata;
};
}
/**
* 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,
* 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) {
return context === previewContext;
}
return PREVIEW_CONTEXT_KEYWORDS.some(keyword => context.includes(keyword));
};
}
export enum PreviewState {
Other = 'other',
@ -164,20 +166,20 @@ export enum PreviewState {
* Retrieve a deploy preview URL from an array of statuses. By default, a
* matching status is inferred via `isPreviewContext`.
*/
export const getPreviewStatus = (
export function getPreviewStatus(
statuses: {
context: string;
target_url: string;
state: PreviewState;
}[],
previewContext: string,
) => {
) {
return statuses.find(({ context }) => {
return isPreviewContext(context, previewContext);
});
};
}
const getConflictingBranches = (branchName: string) => {
function getConflictingBranches(branchName: string) {
// for cms/posts/post-1, conflicting branches are cms/posts, cms
const parts = branchName.split('/');
parts.pop();
@ -188,13 +190,13 @@ const getConflictingBranches = (branchName: string) => {
}, [] as string[]);
return conflictingBranches;
};
}
export const throwOnConflictingBranches = async (
export async function throwOnConflictingBranches(
branchName: string,
getBranch: (name: string) => Promise<{ name: string }>,
apiName: string,
) => {
) {
const possibleConflictingBranches = getConflictingBranches(branchName);
const conflictingBranches = await Promise.all(
@ -213,4 +215,4 @@ export const throwOnConflictingBranches = async (
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.';
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) =>
label.startsWith(getLabelPrefix(labelPrefix));
export const labelToStatus = (label: string, labelPrefix: string) =>
label.substr(getLabelPrefix(labelPrefix).length);
export const statusToLabel = (status: string, labelPrefix: string) =>
`${getLabelPrefix(labelPrefix)}${status}`;
function getLabelPrefix(labelPrefix: string) {
return labelPrefix || DEFAULT_NETLIFY_CMS_LABEL_PREFIX;
}
export const generateContentKey = (collectionName: string, slug: string) =>
`${collectionName}/${slug}`;
export function isCMSLabel(label: string, labelPrefix: string) {
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('/');
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);
};
}
export const branchFromContentKey = (contentKey: string) => {
export function branchFromContentKey(contentKey: string) {
return `${CMS_BRANCH_PREFIX}/${contentKey}`;
};
}

View File

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

View File

@ -21,17 +21,20 @@ describe('parseLinkHeader', () => {
});
describe('getAllResponses', () => {
const generatePulls = length => {
function generatePulls(length) {
return Array.from({ length }, (_, id) => {
return { id: id + 1, number: `134${id}`, state: 'open' };
});
};
}
function createLinkHeaders({ page, pageCount }) {
const pageNum = parseInt(page, 10);
const pageCountNum = parseInt(pageCount, 10);
const url = 'https://api.github.com/pulls';
const link = linkPage => `<${url}?page=${linkPage}>`;
function link(linkPage) {
return `<${url}?page=${linkPage}>`;
}
const linkHeader = oneLine`
${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 const asyncLock = (): AsyncLock => {
export function asyncLock(): AsyncLock {
let lock = semaphore(1);
const acquire = (timeout = 15000) => {
function acquire(timeout = 15000) {
const promise = new Promise<boolean>(resolve => {
// this makes sure a caller doesn't gets stuck forever awaiting on the lock
const timeoutId = setTimeout(() => {
@ -21,9 +21,9 @@ export const asyncLock = (): AsyncLock => {
});
return promise;
};
}
const release = () => {
function release() {
try {
// suppress too many calls to leave error
lock.leave();
@ -37,7 +37,7 @@ export const asyncLock = (): AsyncLock => {
lock = semaphore(1);
}
}
};
}
return { acquire, release };
};
}

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