Merge branch 'main' into next
This commit is contained in:
commit
475024db37
@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"useWorkspaces": true,
|
||||
"version": "1.2.12"
|
||||
"version": "1.2.13"
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@staticcms/app",
|
||||
"version": "1.2.12",
|
||||
"version": "1.2.13",
|
||||
"license": "MIT",
|
||||
"description": "Static CMS application.",
|
||||
"repository": "https://github.com/StaticJsCMS/static-cms",
|
||||
@ -35,7 +35,7 @@
|
||||
"@babel/eslint-parser": "7.21.3",
|
||||
"@babel/runtime": "7.21.0",
|
||||
"@emotion/babel-preset-css-prop": "11.10.0",
|
||||
"@staticcms/core": "^1.2.12",
|
||||
"@staticcms/core": "^1.2.13",
|
||||
"buffer": "6.0.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
@ -141,7 +141,6 @@ collections:
|
||||
label: Pattern Validation
|
||||
widget: code
|
||||
pattern: ['.{12,}', 'Must have at least 12 characters']
|
||||
allow_input: true
|
||||
required: false
|
||||
- name: language
|
||||
label: Language Selection
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@staticcms/core",
|
||||
"version": "1.2.12",
|
||||
"version": "1.2.13",
|
||||
"license": "MIT",
|
||||
"description": "Static CMS core application.",
|
||||
"repository": "https://github.com/StaticJsCMS/static-cms",
|
||||
|
@ -7,7 +7,12 @@ import yaml from 'yaml';
|
||||
import { resolveBackend } from '../backend';
|
||||
import { CONFIG_FAILURE, CONFIG_REQUEST, CONFIG_SUCCESS } from '../constants';
|
||||
import validateConfig from '../constants/configSchema';
|
||||
import { I18N, I18N_FIELD, I18N_STRUCTURE } from '../lib/i18n';
|
||||
import {
|
||||
I18N,
|
||||
I18N_FIELD_NONE,
|
||||
I18N_FIELD_TRANSLATE,
|
||||
I18N_STRUCTURE_SINGLE_FILE,
|
||||
} from '../lib/i18n';
|
||||
import { selectDefaultSortableFields } from '../lib/util/collection.util';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
@ -24,11 +29,13 @@ import type {
|
||||
} from '../interface';
|
||||
import type { RootState } from '../store';
|
||||
|
||||
function isObjectField<F extends BaseField = UnknownField>(field: Field<F>): field is ObjectField {
|
||||
function isObjectField<F extends BaseField = UnknownField>(
|
||||
field: Field<F>,
|
||||
): field is ObjectField<F> {
|
||||
return 'fields' in (field as ObjectField);
|
||||
}
|
||||
|
||||
function isFieldList<F extends BaseField = UnknownField>(field: Field<F>): field is ListField {
|
||||
function isFieldList<F extends BaseField = UnknownField>(field: Field<F>): field is ListField<F> {
|
||||
return 'types' in (field as ListField) || 'field' in (field as ListField);
|
||||
}
|
||||
|
||||
@ -70,9 +77,9 @@ function setDefaultPublicFolderForField<T extends Field>(field: T) {
|
||||
|
||||
function setI18nField<T extends Field>(field: T) {
|
||||
if (field[I18N] === true) {
|
||||
return { ...field, [I18N]: I18N_FIELD.TRANSLATE };
|
||||
return { ...field, [I18N]: I18N_FIELD_TRANSLATE };
|
||||
} else if (field[I18N] === false || !field[I18N]) {
|
||||
return { ...field, [I18N]: I18N_FIELD.NONE };
|
||||
return { ...field, [I18N]: I18N_FIELD_NONE };
|
||||
}
|
||||
return field;
|
||||
}
|
||||
@ -104,9 +111,9 @@ function setI18nDefaultsForFields(collectionOrFileFields: Field[], hasI18n: bool
|
||||
}
|
||||
|
||||
function throwOnInvalidFileCollectionStructure(i18n?: I18nInfo) {
|
||||
if (i18n && i18n.structure !== I18N_STRUCTURE.SINGLE_FILE) {
|
||||
if (i18n && i18n.structure !== I18N_STRUCTURE_SINGLE_FILE) {
|
||||
throw new Error(
|
||||
`i18n configuration for files collections is limited to ${I18N_STRUCTURE.SINGLE_FILE} structure`,
|
||||
`i18n configuration for files collections is limited to ${I18N_STRUCTURE_SINGLE_FILE} structure`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,13 @@ import {
|
||||
SORT_ENTRIES_SUCCESS,
|
||||
} from '../constants';
|
||||
import ValidationErrorTypes from '../constants/validationErrorTypes';
|
||||
import { duplicateDefaultI18nFields, hasI18n, I18N_FIELD, serializeI18n } from '../lib/i18n';
|
||||
import {
|
||||
duplicateDefaultI18nFields,
|
||||
hasI18n,
|
||||
I18N_FIELD_DUPLICATE,
|
||||
I18N_FIELD_TRANSLATE,
|
||||
serializeI18n,
|
||||
} from '../lib/i18n';
|
||||
import { serializeValues } from '../lib/serializeEntryValues';
|
||||
import { Cursor } from '../lib/util';
|
||||
import { selectFields, updateFieldByKey } from '../lib/util/collection.util';
|
||||
@ -899,7 +905,7 @@ function createEmptyDraftI18nData(collection: Collection, dataFields: Field[]) {
|
||||
}
|
||||
|
||||
function skipField(field: Field) {
|
||||
return field.i18n !== I18N_FIELD.DUPLICATE && field.i18n !== I18N_FIELD.TRANSLATE;
|
||||
return field.i18n !== I18N_FIELD_DUPLICATE && field.i18n !== I18N_FIELD_TRANSLATE;
|
||||
}
|
||||
|
||||
const i18nData = createEmptyDraftData(dataFields, skipField);
|
||||
|
@ -8,7 +8,14 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { formatExtensions, frontmatterFormats, extensionFormatters } from '../formats/formats';
|
||||
import { getWidgets } from '../lib/registry';
|
||||
import { I18N_STRUCTURE, I18N_FIELD } from '../lib/i18n';
|
||||
import {
|
||||
I18N_FIELD_DUPLICATE,
|
||||
I18N_FIELD_NONE,
|
||||
I18N_FIELD_TRANSLATE,
|
||||
I18N_STRUCTURE_MULTIPLE_FILES,
|
||||
I18N_STRUCTURE_MULTIPLE_FOLDERS,
|
||||
I18N_STRUCTURE_SINGLE_FILE,
|
||||
} from '../lib/i18n';
|
||||
|
||||
import type { ErrorObject } from 'ajv';
|
||||
import type { Config } from '../interface';
|
||||
@ -18,7 +25,14 @@ const localeType = { type: 'string', minLength: 2, maxLength: 10, pattern: '^[a-
|
||||
const i18n = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
structure: { type: 'string', enum: Object.values(I18N_STRUCTURE) },
|
||||
structure: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
I18N_STRUCTURE_MULTIPLE_FILES,
|
||||
I18N_STRUCTURE_MULTIPLE_FOLDERS,
|
||||
I18N_STRUCTURE_SINGLE_FILE,
|
||||
],
|
||||
},
|
||||
locales: {
|
||||
type: 'array',
|
||||
minItems: 2,
|
||||
@ -39,7 +53,10 @@ const i18nCollection = {
|
||||
};
|
||||
|
||||
const i18nField = {
|
||||
oneOf: [{ type: 'boolean' }, { type: 'string', enum: Object.values(I18N_FIELD) }],
|
||||
oneOf: [
|
||||
{ type: 'boolean' },
|
||||
{ type: 'string', enum: [I18N_FIELD_DUPLICATE, I18N_FIELD_NONE, I18N_FIELD_TRANSLATE] },
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -16,7 +16,14 @@ import type {
|
||||
SORT_DIRECTION_NONE,
|
||||
} from './constants';
|
||||
import type { formatExtensions } from './formats/formats';
|
||||
import type { I18N_STRUCTURE } from './lib/i18n';
|
||||
import type {
|
||||
I18N_FIELD_DUPLICATE,
|
||||
I18N_FIELD_NONE,
|
||||
I18N_FIELD_TRANSLATE,
|
||||
I18N_STRUCTURE_MULTIPLE_FILES,
|
||||
I18N_STRUCTURE_MULTIPLE_FOLDERS,
|
||||
I18N_STRUCTURE_SINGLE_FILE,
|
||||
} from './lib/i18n';
|
||||
import type { AllowedEvent } from './lib/registry';
|
||||
import type Cursor from './lib/util/Cursor';
|
||||
import type AssetProxy from './valueObjects/AssetProxy';
|
||||
@ -566,7 +573,7 @@ export interface CodeField extends BaseField {
|
||||
keys?: { code: string; lang: string };
|
||||
output_code_only?: boolean;
|
||||
|
||||
code_mirror_config: {
|
||||
code_mirror_config?: {
|
||||
extra_keys?: Record<string, string>;
|
||||
} & Record<string, unknown>;
|
||||
}
|
||||
@ -689,11 +696,11 @@ export type Field<EF extends BaseField = UnknownField> =
|
||||
| ColorField
|
||||
| DateTimeField
|
||||
| FileOrImageField
|
||||
| ListField
|
||||
| ListField<EF>
|
||||
| MapField
|
||||
| MarkdownField
|
||||
| NumberField
|
||||
| ObjectField
|
||||
| ObjectField<EF>
|
||||
| RelationField
|
||||
| SelectField
|
||||
| HiddenField
|
||||
@ -704,7 +711,7 @@ export interface ViewFilter {
|
||||
id?: string;
|
||||
label: string;
|
||||
field: string;
|
||||
pattern: string;
|
||||
pattern: string | boolean | number;
|
||||
}
|
||||
|
||||
export interface ViewGroup {
|
||||
@ -853,10 +860,20 @@ export interface EditorPersistOptions {
|
||||
duplicate?: boolean;
|
||||
}
|
||||
|
||||
export type I18nStructure =
|
||||
| typeof I18N_STRUCTURE_MULTIPLE_FILES
|
||||
| typeof I18N_STRUCTURE_MULTIPLE_FOLDERS
|
||||
| typeof I18N_STRUCTURE_SINGLE_FILE;
|
||||
|
||||
export type I18nField =
|
||||
| typeof I18N_FIELD_DUPLICATE
|
||||
| typeof I18N_FIELD_TRANSLATE
|
||||
| typeof I18N_FIELD_NONE;
|
||||
|
||||
export interface I18nInfo {
|
||||
locales: string[];
|
||||
defaultLocale: string;
|
||||
structure?: I18N_STRUCTURE;
|
||||
structure?: I18nStructure;
|
||||
}
|
||||
|
||||
export interface ProcessedCodeLanguage {
|
||||
|
@ -5,22 +5,26 @@ import groupBy from 'lodash/groupBy';
|
||||
import { selectEntrySlug } from './util/collection.util';
|
||||
import { set } from './util/object.util';
|
||||
|
||||
import type { Field, Collection, Entry, EntryData, i18nCollection, I18nInfo } from '../interface';
|
||||
import type {
|
||||
Field,
|
||||
Collection,
|
||||
Entry,
|
||||
EntryData,
|
||||
i18nCollection,
|
||||
I18nInfo,
|
||||
I18nStructure,
|
||||
} from '../interface';
|
||||
import type { EntryDraftState } from '../reducers/entryDraft';
|
||||
|
||||
export const I18N = 'i18n';
|
||||
|
||||
export enum I18N_STRUCTURE {
|
||||
MULTIPLE_FOLDERS = 'multiple_folders',
|
||||
MULTIPLE_FILES = 'multiple_files',
|
||||
SINGLE_FILE = 'single_file',
|
||||
}
|
||||
export const I18N_STRUCTURE_MULTIPLE_FOLDERS = 'multiple_folders';
|
||||
export const I18N_STRUCTURE_MULTIPLE_FILES = 'multiple_files';
|
||||
export const I18N_STRUCTURE_SINGLE_FILE = 'single_file';
|
||||
|
||||
export enum I18N_FIELD {
|
||||
TRANSLATE = 'translate',
|
||||
DUPLICATE = 'duplicate',
|
||||
NONE = 'none',
|
||||
}
|
||||
export const I18N_FIELD_TRANSLATE = 'translate';
|
||||
export const I18N_FIELD_DUPLICATE = 'duplicate';
|
||||
export const I18N_FIELD_NONE = 'none';
|
||||
|
||||
export function hasI18n(collection: Collection | i18nCollection): collection is i18nCollection {
|
||||
return I18N in collection;
|
||||
@ -37,22 +41,22 @@ export function getI18nInfo(collection: Collection | i18nCollection): I18nInfo |
|
||||
|
||||
export function getI18nFilesDepth(collection: Collection, depth: number) {
|
||||
const { structure } = getI18nInfo(collection) as I18nInfo;
|
||||
if (structure === I18N_STRUCTURE.MULTIPLE_FOLDERS) {
|
||||
if (structure === I18N_STRUCTURE_MULTIPLE_FOLDERS) {
|
||||
return depth + 1;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
export function isFieldTranslatable(field: Field, locale?: string, defaultLocale?: string) {
|
||||
return locale !== defaultLocale && field.i18n === I18N_FIELD.TRANSLATE;
|
||||
return locale !== defaultLocale && field.i18n === I18N_FIELD_TRANSLATE;
|
||||
}
|
||||
|
||||
export function isFieldDuplicate(field: Field, locale?: string, defaultLocale?: string) {
|
||||
return locale !== defaultLocale && field.i18n === I18N_FIELD.DUPLICATE;
|
||||
return locale !== defaultLocale && field.i18n === I18N_FIELD_DUPLICATE;
|
||||
}
|
||||
|
||||
export function isFieldHidden(field: Field, locale?: string, defaultLocale?: string) {
|
||||
return locale !== defaultLocale && field.i18n === I18N_FIELD.NONE;
|
||||
return locale !== defaultLocale && field.i18n === I18N_FIELD_NONE;
|
||||
}
|
||||
|
||||
export function getLocaleDataPath(locale: string) {
|
||||
@ -65,37 +69,37 @@ export function getDataPath(locale: string, defaultLocale: string) {
|
||||
}
|
||||
|
||||
export function getFilePath(
|
||||
structure: I18N_STRUCTURE,
|
||||
structure: I18nStructure,
|
||||
extension: string,
|
||||
path: string,
|
||||
slug: string,
|
||||
locale: string,
|
||||
) {
|
||||
switch (structure) {
|
||||
case I18N_STRUCTURE.MULTIPLE_FOLDERS:
|
||||
case I18N_STRUCTURE_MULTIPLE_FOLDERS:
|
||||
return path.replace(`/${slug}`, `/${locale}/${slug}`);
|
||||
case I18N_STRUCTURE.MULTIPLE_FILES:
|
||||
case I18N_STRUCTURE_MULTIPLE_FILES:
|
||||
return path.replace(new RegExp(`${escapeRegExp(extension)}$`), `${locale}.${extension}`);
|
||||
case I18N_STRUCTURE.SINGLE_FILE:
|
||||
case I18N_STRUCTURE_SINGLE_FILE:
|
||||
default:
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocaleFromPath(structure: I18N_STRUCTURE, extension: string, path: string) {
|
||||
export function getLocaleFromPath(structure: I18nStructure, extension: string, path: string) {
|
||||
switch (structure) {
|
||||
case I18N_STRUCTURE.MULTIPLE_FOLDERS: {
|
||||
case I18N_STRUCTURE_MULTIPLE_FOLDERS: {
|
||||
const parts = path.split('/');
|
||||
// filename
|
||||
parts.pop();
|
||||
// locale
|
||||
return parts.pop();
|
||||
}
|
||||
case I18N_STRUCTURE.MULTIPLE_FILES: {
|
||||
case I18N_STRUCTURE_MULTIPLE_FILES: {
|
||||
const parts = path.slice(0, -`.${extension}`.length);
|
||||
return parts.split('.').pop();
|
||||
}
|
||||
case I18N_STRUCTURE.SINGLE_FILE:
|
||||
case I18N_STRUCTURE_SINGLE_FILE:
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@ -109,24 +113,24 @@ export function getFilePaths(
|
||||
) {
|
||||
const { structure, locales } = getI18nInfo(collection) as I18nInfo;
|
||||
|
||||
if (structure === I18N_STRUCTURE.SINGLE_FILE) {
|
||||
if (structure === I18N_STRUCTURE_SINGLE_FILE) {
|
||||
return [path];
|
||||
}
|
||||
|
||||
const paths = locales.map(locale =>
|
||||
getFilePath(structure as I18N_STRUCTURE, extension, path, slug, locale),
|
||||
getFilePath(structure as I18nStructure, extension, path, slug, locale),
|
||||
);
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
export function normalizeFilePath(structure: I18N_STRUCTURE, path: string, locale: string) {
|
||||
export function normalizeFilePath(structure: I18nStructure, path: string, locale: string) {
|
||||
switch (structure) {
|
||||
case I18N_STRUCTURE.MULTIPLE_FOLDERS:
|
||||
case I18N_STRUCTURE_MULTIPLE_FOLDERS:
|
||||
return path.replace(`${locale}/`, '');
|
||||
case I18N_STRUCTURE.MULTIPLE_FILES:
|
||||
case I18N_STRUCTURE_MULTIPLE_FILES:
|
||||
return path.replace(`.${locale}`, '');
|
||||
case I18N_STRUCTURE.SINGLE_FILE:
|
||||
case I18N_STRUCTURE_SINGLE_FILE:
|
||||
default:
|
||||
return path;
|
||||
}
|
||||
@ -142,12 +146,12 @@ export function getI18nFiles(
|
||||
newPath?: string,
|
||||
) {
|
||||
const {
|
||||
structure = I18N_STRUCTURE.SINGLE_FILE,
|
||||
structure = I18N_STRUCTURE_SINGLE_FILE,
|
||||
defaultLocale,
|
||||
locales,
|
||||
} = getI18nInfo(collection) as I18nInfo;
|
||||
|
||||
if (structure === I18N_STRUCTURE.SINGLE_FILE) {
|
||||
if (structure === I18N_STRUCTURE_SINGLE_FILE) {
|
||||
const data = locales.reduce((map, locale) => {
|
||||
const dataPath = getDataPath(locale, defaultLocale);
|
||||
if (map) {
|
||||
@ -230,7 +234,7 @@ export function formatI18nBackup(
|
||||
|
||||
function mergeValues(
|
||||
collection: Collection,
|
||||
structure: I18N_STRUCTURE,
|
||||
structure: I18nStructure,
|
||||
defaultLocale: string,
|
||||
values: { locale: string; value: Entry }[],
|
||||
) {
|
||||
@ -285,13 +289,13 @@ export async function getI18nEntry(
|
||||
getEntryValue: (path: string) => Promise<Entry>,
|
||||
) {
|
||||
const {
|
||||
structure = I18N_STRUCTURE.SINGLE_FILE,
|
||||
structure = I18N_STRUCTURE_SINGLE_FILE,
|
||||
locales,
|
||||
defaultLocale,
|
||||
} = getI18nInfo(collection) as I18nInfo;
|
||||
|
||||
let entryValue: Entry;
|
||||
if (structure === I18N_STRUCTURE.SINGLE_FILE) {
|
||||
if (structure === I18N_STRUCTURE_SINGLE_FILE) {
|
||||
entryValue = mergeSingleFileValue(await getEntryValue(path), defaultLocale, locales);
|
||||
} else {
|
||||
const entryValues = await Promise.all(
|
||||
@ -315,11 +319,11 @@ export async function getI18nEntry(
|
||||
|
||||
export function groupEntries(collection: Collection, extension: string, entries: Entry[]): Entry[] {
|
||||
const {
|
||||
structure = I18N_STRUCTURE.SINGLE_FILE,
|
||||
structure = I18N_STRUCTURE_SINGLE_FILE,
|
||||
defaultLocale,
|
||||
locales,
|
||||
} = getI18nInfo(collection) as I18nInfo;
|
||||
if (structure === I18N_STRUCTURE.SINGLE_FILE) {
|
||||
if (structure === I18N_STRUCTURE_SINGLE_FILE) {
|
||||
return entries.map(e => mergeSingleFileValue(e, defaultLocale, locales));
|
||||
}
|
||||
|
||||
@ -349,7 +353,7 @@ export function getI18nDataFiles(
|
||||
diffFiles: { path: string; id: string; newFile: boolean }[],
|
||||
) {
|
||||
const { structure } = getI18nInfo(collection) as I18nInfo;
|
||||
if (structure === I18N_STRUCTURE.SINGLE_FILE) {
|
||||
if (structure === I18N_STRUCTURE_SINGLE_FILE) {
|
||||
return diffFiles;
|
||||
}
|
||||
const paths = getFilePaths(collection, extension, path, slug);
|
||||
@ -386,7 +390,7 @@ export function duplicateI18nFields(
|
||||
fieldPath: string,
|
||||
) {
|
||||
const value = get(entryDraft, ['entry', 'data', ...fieldPath.split('.')]);
|
||||
if (field.i18n === I18N_FIELD.DUPLICATE) {
|
||||
if (field.i18n === I18N_FIELD_DUPLICATE) {
|
||||
locales
|
||||
.filter(l => l !== defaultLocale)
|
||||
.forEach(l => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user