This commit is contained in:
2025-08-25 20:24:23 +08:00
parent 30106e0129
commit 0ae8d7a709
1044 changed files with 321581 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.2.1](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@3.2.0...decap-cms-lib-widgets@3.2.1) (2025-07-10)
**Note:** Version bump only for package decap-cms-lib-widgets
# [3.2.0](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@3.1.0...decap-cms-lib-widgets@3.2.0) (2025-06-26)
**Note:** Version bump only for package decap-cms-lib-widgets
# [3.1.0](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@3.0.2...decap-cms-lib-widgets@3.1.0) (2024-11-12)
### Bug Fixes
- summary string transformations format ([#7221](https://github.com/decaporg/decap-cms/issues/7221)) ([#7313](https://github.com/decaporg/decap-cms/issues/7313)) ([1d0cd61](https://github.com/decaporg/decap-cms/commit/1d0cd611812860450ff15b31eee7f764d6026306))
## [3.0.2](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@3.0.1...decap-cms-lib-widgets@3.0.2) (2024-03-21)
**Note:** Version bump only for package decap-cms-lib-widgets
## [3.0.1](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@3.0.1-beta.2...decap-cms-lib-widgets@3.0.1) (2024-02-01)
**Note:** Version bump only for package decap-cms-lib-widgets
## [3.0.1-beta.2](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@3.0.1-beta.1...decap-cms-lib-widgets@3.0.1-beta.2) (2024-01-31)
**Note:** Version bump only for package decap-cms-lib-widgets
## [3.0.1-beta.1](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@3.0.1-beta.0...decap-cms-lib-widgets@3.0.1-beta.1) (2024-01-16)
### Bug Fixes
- change dayjs to per-package dependency ([#6992](https://github.com/decaporg/decap-cms/issues/6992)) ([0c278b0](https://github.com/decaporg/decap-cms/commit/0c278b0a83d93233d3b3e860d3029df20fe1c501))
## [3.0.1-beta.0](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@3.0.0...decap-cms-lib-widgets@3.0.1-beta.0) (2023-11-23)
### Performance Improvements
- replace moment with dayjs ([#6980](https://github.com/decaporg/decap-cms/issues/6980)) ([22370b1](https://github.com/decaporg/decap-cms/commit/22370b13e49a4a5f58a60ebd4bc40ce4b141eb11))
# [3.0.0](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@1.9.0...decap-cms-lib-widgets@3.0.0) (2023-08-18)
**Note:** Version bump only for package decap-cms-lib-widgets
# [1.9.0](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@1.9.0-beta.0...decap-cms-lib-widgets@1.9.0) (2023-08-18)
**Note:** Version bump only for package decap-cms-lib-widgets
# 1.9.0-beta.0 (2023-08-18)
### Features
- rename packages ([#6863](https://github.com/decaporg/decap-cms/issues/6863)) ([d515e7b](https://github.com/decaporg/decap-cms/commit/d515e7bd33216a775d96887b08c4f7b1962941bb))
## [1.8.2-beta.0](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@1.8.1...decap-cms-lib-widgets@1.8.2-beta.0) (2023-07-27)
**Note:** Version bump only for package decap-cms-lib-widgets
## [1.8.1](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@1.8.0...decap-cms-lib-widgets@1.8.1) (2022-04-13)
**Note:** Version bump only for package decap-cms-lib-widgets
# [1.8.0](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@1.7.0...decap-cms-lib-widgets@1.8.0) (2022-01-21)
### Features
- add truncate filter to summary tag ([#6105](https://github.com/decaporg/decap-cms/issues/6105)) ([d66c573](https://github.com/decaporg/decap-cms/commit/d66c573697c6a66919e048f0fde9cf2f8ea6acac))
# [1.7.0](https://github.com/decaporg/decap-cms/compare/decap-cms-lib-widgets@1.6.3...decap-cms-lib-widgets@1.7.0) (2021-10-11)
### Features
- add string template filters "default" and "ternary" ([#3677](https://github.com/decaporg/decap-cms/issues/3677)) ([#5878](https://github.com/decaporg/decap-cms/issues/5878)) ([c791158](https://github.com/decaporg/decap-cms/commit/c791158dd5ea8ea03930f9881a86c71cb1770836))
## [1.6.3](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/compare/decap-cms-lib-widgets@1.6.2...decap-cms-lib-widgets@1.6.3) (2021-06-01)
**Note:** Version bump only for package decap-cms-lib-widgets
## [1.6.2](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/compare/decap-cms-lib-widgets@1.6.1...decap-cms-lib-widgets@1.6.2) (2021-05-31)
**Note:** Version bump only for package decap-cms-lib-widgets
## [1.6.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/compare/decap-cms-lib-widgets@1.6.0...decap-cms-lib-widgets@1.6.1) (2021-02-10)
**Note:** Version bump only for package decap-cms-lib-widgets
# [1.6.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/compare/decap-cms-lib-widgets@1.5.0...decap-cms-lib-widgets@1.6.0) (2020-10-25)
### Features
- Support filters for template strings [#3677](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/issues/3677) ([#4396](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/issues/4396)) ([1fa108e](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/commit/1fa108ee67b7e992a4d2a61cde13df7917e103be))
# [1.5.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/compare/decap-cms-lib-widgets@1.4.0...decap-cms-lib-widgets@1.5.0) (2020-10-20)
### Features
- **widget-list:** add min max configuration ([#4394](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/issues/4394)) ([5fdfe40](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/commit/5fdfe40dd29e9e22c9ae7d6219bc057f7ea7280b))
# [1.4.0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/compare/decap-cms-lib-widgets@1.3.5...decap-cms-lib-widgets@1.4.0) (2020-09-28)
### Features
- **core:** Add {{dirname}} to summary and preview_path ([#4279](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/issues/4279)) ([576e4f0](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/commit/576e4f0f1a158d6b587587c52fb288d8f6eea89f))
## [1.3.5](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/compare/decap-cms-lib-widgets@1.3.4...decap-cms-lib-widgets@1.3.5) (2020-09-15)
**Note:** Version bump only for package decap-cms-lib-widgets
## 1.3.4 (2020-09-08)
### Reverts
- Revert "chore(release): publish" ([828bb16](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/commit/828bb16415b8c22a34caa19c50c38b24ffe9ceae))
## 1.3.3 (2020-08-20)
### Reverts
- Revert "chore(release): publish" ([8262487](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/commit/82624879ccbcb16610090041db28f00714d924c8))
## 1.3.2 (2020-07-27)
### Reverts
- Revert "chore(release): publish" ([118d50a](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/commit/118d50a7a70295f25073e564b5161aa2b9883056))
## [1.3.1](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/compare/decap-cms-lib-widgets@1.3.0...decap-cms-lib-widgets@1.3.1) (2020-07-14)
### Bug Fixes
- relation widget performance ([#3975](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/issues/3975)) ([c7e0fe8](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/commit/c7e0fe8492d09a3d151c608f50da844f421362ed))
# 1.3.0 (2020-05-04)
### Features
- **widget-relation:** string templates support ([#3659](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/issues/3659)) ([213ae86](https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets/commit/213ae86b54d02f5fc79fe11113507587ed062ff2))

View File

@@ -0,0 +1,9 @@
# Docs coming soon!
Decap CMS was converted from a single npm package to a "monorepo" of over 20 packages.
We haven't created a README for this package yet, but you can:
1. Check out the [main readme](https://github.com/decaporg/decap-cms/#readme) or the [documentation
site](https://www.decapcms.org) for more info.
2. Reach out to the [community chat](https://decapcms.org/chat/) if you need help.
3. Help out and [write the readme yourself](https://github.com/decaporg/decap-cms/edit/main/packages/decap-cms-lib-widgets/README.md)!

View File

@@ -0,0 +1,30 @@
{
"name": "decap-cms-lib-widgets",
"description": "Shared utilities for Decap CMS.",
"version": "3.2.1",
"repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-widgets",
"bugs": "https://github.com/decaporg/decap-cms/issues",
"module": "dist/esm/index.js",
"main": "dist/decap-cms-lib-widgets.js",
"license": "MIT",
"keywords": [
"decap-cms"
],
"sideEffects": false,
"scripts": {
"develop": "npm run build:esm -- --watch",
"build": "cross-env NODE_ENV=production webpack",
"build:esm": "cross-env NODE_ENV=esm babel src --out-dir dist/esm --ignore \"**/__tests__\" --root-mode upward --extensions \".js,.jsx,.ts,.tsx\""
},
"dependencies": {
"dayjs": "^1.11.10",
"path-browserify": "^1.0.1"
},
"peerDependencies": {
"immutable": "^3.7.6",
"lodash": "^4.17.11"
},
"browser": {
"path": "path-browserify"
}
}

View File

@@ -0,0 +1,241 @@
import { fromJS } from 'immutable';
import {
compileStringTemplate,
expandPath,
extractTemplateVars,
keyToPathArray,
parseDateFromEntry,
} from '../stringTemplate';
describe('stringTemplate', () => {
describe('keyToPathArray', () => {
it('should return array of length 1 with simple path', () => {
expect(keyToPathArray('category')).toEqual(['category']);
});
it('should return path array for complex path', () => {
expect(keyToPathArray('categories[0].title.subtitles[0].welcome[2]')).toEqual([
'categories',
'0',
'title',
'subtitles',
'0',
'welcome',
'2',
]);
});
});
describe('parseDateFromEntry', () => {
it('should return date based on dateFieldName', () => {
const date = new Date().toISOString();
const dateFieldName = 'dateFieldName';
const entry = fromJS({ data: { dateFieldName: date } });
expect(parseDateFromEntry(entry, dateFieldName).toISOString()).toBe(date);
});
it('should return undefined on empty dateFieldName', () => {
const entry = fromJS({ data: {} });
expect(parseDateFromEntry(entry, '')).toBeUndefined();
expect(parseDateFromEntry(entry, null)).toBeUndefined();
expect(parseDateFromEntry(entry, undefined)).toBeUndefined();
});
it('should return undefined on invalid date', () => {
const entry = fromJS({ data: { date: '' } });
const dateFieldName = 'date';
expect(parseDateFromEntry(entry, dateFieldName)).toBeUndefined();
});
});
describe('extractTemplateVars', () => {
it('should extract template variables', () => {
expect(extractTemplateVars('{{slug}}-hello-{{date}}-world-{{fields.id}}')).toEqual([
'slug',
'date',
'fields.id',
]);
});
it('should return empty array on no matches', () => {
expect(extractTemplateVars('hello-world')).toEqual([]);
});
});
describe('compileStringTemplate', () => {
const date = new Date('2020-01-02T13:28:27.679Z');
it('should compile year variable', () => {
expect(compileStringTemplate('{{year}}', date)).toBe('2020');
});
it('should compile month variable', () => {
expect(compileStringTemplate('{{month}}', date)).toBe('01');
});
it('should compile day variable', () => {
expect(compileStringTemplate('{{day}}', date)).toBe('02');
});
it('should compile hour variable', () => {
expect(compileStringTemplate('{{hour}}', date)).toBe('13');
});
it('should compile minute variable', () => {
expect(compileStringTemplate('{{minute}}', date)).toBe('28');
});
it('should compile second variable', () => {
expect(compileStringTemplate('{{second}}', date)).toBe('27');
});
it('should error on missing date', () => {
expect(() => compileStringTemplate('{{year}}')).toThrowError();
});
it('return compiled template', () => {
expect(
compileStringTemplate(
'{{slug}}-{{year}}-{{fields.slug}}-{{title}}-{{date}}',
date,
'backendSlug',
fromJS({ slug: 'entrySlug', title: 'title', date }),
),
).toBe('backendSlug-2020-entrySlug-title-' + date.toString());
});
it('return apply processor to values', () => {
expect(
compileStringTemplate('{{slug}}', date, 'slug', fromJS({}), value => value.toUpperCase()),
).toBe('SLUG');
});
it('return apply filter to values', () => {
expect(
compileStringTemplate(
'{{slug | upper}}-{{title | lower}}-{{year}}',
date,
'backendSlug',
fromJS({ slug: 'entrySlug', title: 'Title', date }),
),
).toBe('BACKENDSLUG-title-2020');
});
it('return apply filter to date field', () => {
expect(
compileStringTemplate(
"{{slug | upper}}-{{title | lower}}-{{published | date('MM-DD')}}-{{year}}",
date,
'backendSlug',
fromJS({ slug: 'entrySlug', title: 'Title', published: date, date }),
),
).toBe('BACKENDSLUG-title-01-02-2020');
});
it('return apply filter for default value', () => {
expect(
compileStringTemplate(
"{{slug | upper}}-{{title | default('none')}}-{{subtitle | default('none')}}",
date,
'backendSlug',
fromJS({ slug: 'entrySlug', title: 'title', subtitle: null, published: date, date }),
),
).toBe('BACKENDSLUG-title-none');
});
it('return apply filter for ternary', () => {
expect(
compileStringTemplate(
"{{slug | upper}}-{{starred | ternary('star','nostar')}}-{{done | ternary('done', 'open')}}",
date,
'backendSlug',
fromJS({ slug: 'entrySlug', starred: true, done: false }),
),
).toBe('BACKENDSLUG-star-open');
});
it('return apply filter for truncate', () => {
expect(
compileStringTemplate(
'{{slug | truncate(6)}}',
date,
'backendSlug',
fromJS({ slug: 'entrySlug', starred: true, done: false }),
),
).toBe('backen...');
});
it('return apply filter for truncate', () => {
expect(
compileStringTemplate(
"{{slug | truncate(3,'***')}}",
date,
'backendSlug',
fromJS({ slug: 'entrySlug', starred: true, done: false }),
),
).toBe('bac***');
});
});
describe('expandPath', () => {
it('should expand wildcard paths', () => {
const data = {
categories: [
{
name: 'category 1',
},
{
name: 'category 2',
},
],
};
expect(expandPath({ data, path: 'categories.*.name' })).toEqual([
'categories.0.name',
'categories.1.name',
]);
});
it('should handle wildcard at the end of the path', () => {
const data = {
nested: {
otherNested: {
list: [
{
title: 'title 1',
nestedList: [{ description: 'description 1' }, { description: 'description 2' }],
},
{
title: 'title 2',
nestedList: [{ description: 'description 2' }, { description: 'description 2' }],
},
],
},
},
};
expect(expandPath({ data, path: 'nested.otherNested.list.*.nestedList.*' })).toEqual([
'nested.otherNested.list.0.nestedList.0',
'nested.otherNested.list.0.nestedList.1',
'nested.otherNested.list.1.nestedList.0',
'nested.otherNested.list.1.nestedList.1',
]);
});
it('should handle non wildcard index', () => {
const data = {
categories: [
{
name: 'category 1',
},
{
name: 'category 2',
},
],
};
const path = 'categories.0.name';
expect(expandPath({ data, path })).toEqual(['categories.0.name']);
});
});
});

View File

@@ -0,0 +1,8 @@
import * as stringTemplate from './stringTemplate';
import * as validations from './validations';
export const DecapCmsLibWidgets = {
stringTemplate,
validations,
};
export { stringTemplate, validations };

View File

@@ -0,0 +1,250 @@
import { Map } from 'immutable';
import get from 'lodash/get';
import trimEnd from 'lodash/trimEnd';
import truncate from 'lodash/truncate';
import dayjs from 'dayjs';
import { basename, dirname, extname } from 'path';
const filters = [
{ pattern: /^upper$/, transform: (str: string) => str.toUpperCase() },
{
pattern: /^lower$/,
transform: (str: string) => str.toLowerCase(),
},
{
pattern: /^date\('(.+)'\)$/,
transform: (str: string, match: RegExpMatchArray) => dayjs(str).format(match[1]),
},
{
pattern: /^default\('(.+)'\)$/,
transform: (str: string, match: RegExpMatchArray) => (str ? str : match[1]),
},
{
pattern: /^ternary\('(.*)',\s*'(.*)'\)$/,
transform: (str: string, match: RegExpMatchArray) => (str ? match[1] : match[2]),
},
{
pattern: /^truncate\(([0-9]+)(?:(?:,\s*['"])([^'"]*)(?:['"]))?\)$/,
transform: (str: string, match: RegExpMatchArray) => {
const omission = match[2] || '...';
const length = parseInt(match[1]) + omission.length;
return truncate(str, {
length,
omission,
});
},
},
];
const FIELD_PREFIX = 'fields.';
const templateContentPattern = ' *([^}{| ]+)';
const filterPattern = '( \\| ([^}{]+?))? *';
const templateVariablePattern = `{{${templateContentPattern}${filterPattern}}}`;
// prepends a Zero if the date has only 1 digit
function formatDate(date: number) {
return `0${date}`.slice(-2);
}
export const dateParsers: Record<string, (date: Date) => string> = {
year: (date: Date) => `${date.getUTCFullYear()}`,
month: (date: Date) => formatDate(date.getUTCMonth() + 1),
day: (date: Date) => formatDate(date.getUTCDate()),
hour: (date: Date) => formatDate(date.getUTCHours()),
minute: (date: Date) => formatDate(date.getUTCMinutes()),
second: (date: Date) => formatDate(date.getUTCSeconds()),
};
export function parseDateFromEntry(entry: Map<string, unknown>, dateFieldName?: string | null) {
if (!dateFieldName) {
return;
}
const dateValue = entry.getIn(['data', dateFieldName]);
const dateDayjs = dateValue && dayjs(dateValue);
if (dateDayjs && dateDayjs.isValid()) {
return dateDayjs.toDate();
}
}
export const SLUG_MISSING_REQUIRED_DATE = 'SLUG_MISSING_REQUIRED_DATE';
export function keyToPathArray(key?: string) {
if (!key) {
return [];
}
const parts = [];
const separator = '';
const chars = key.split(separator);
let currentChar;
let currentStr = [];
while ((currentChar = chars.shift())) {
if (['[', ']', '.'].includes(currentChar)) {
if (currentStr.length > 0) {
parts.push(currentStr.join(separator));
}
currentStr = [];
} else {
currentStr.push(currentChar);
}
}
if (currentStr.length > 0) {
parts.push(currentStr.join(separator));
}
return parts;
}
export function expandPath({
data,
path,
paths = [],
}: {
data: Record<string, unknown>;
path: string;
paths?: string[];
}) {
if (path.endsWith('.*')) {
path = path + '.';
}
const sep = '.*.';
const parts = path.split(sep);
if (parts.length === 1) {
paths.push(path);
} else {
const partialPath = parts[0];
const value = get(data, partialPath);
if (Array.isArray(value)) {
value.forEach((_, index) => {
expandPath({
data,
path: trimEnd(`${partialPath}.${index}.${parts.slice(1).join(sep)}`, '.'),
paths,
});
});
}
}
return paths;
}
// Allow `fields.` prefix in placeholder to override built in replacements
// like "slug" and "year" with values from fields of the same name.
function getExplicitFieldReplacement(key: string, data: Map<string, unknown>) {
if (!key.startsWith(FIELD_PREFIX)) {
return;
}
const fieldName = key.slice(FIELD_PREFIX.length);
const value = data.getIn(keyToPathArray(fieldName));
if (typeof value === 'object' && value !== null) {
return JSON.stringify(value);
}
return value;
}
function getFilterFunction(filterStr: string) {
if (filterStr) {
let match: RegExpMatchArray | null = null;
const filter = filters.find(filter => {
match = filterStr.match(filter.pattern);
return !!match;
});
if (filter) {
return (str: string) => filter.transform(str, match as RegExpMatchArray);
}
}
return null;
}
export function compileStringTemplate(
template: string,
date: Date | undefined | null,
identifier = '',
data = Map<string, unknown>(),
processor?: (value: string) => string,
) {
let missingRequiredDate;
// Turn off date processing (support for replacements like `{{year}}`), by passing in
// `null` as the date arg.
const useDate = date !== null;
const compiledString = template.replace(
RegExp(templateVariablePattern, 'g'),
(_full, key: string, _part, filter: string) => {
let replacement;
const explicitFieldReplacement = getExplicitFieldReplacement(key, data);
if (explicitFieldReplacement) {
replacement = explicitFieldReplacement;
} else if (dateParsers[key] && !date) {
missingRequiredDate = true;
return '';
} else if (dateParsers[key]) {
replacement = dateParsers[key](date as Date);
} else if (key === 'slug') {
replacement = identifier;
} else {
replacement = data.getIn(keyToPathArray(key), '') as string;
}
if (processor) {
return processor(replacement);
} else {
const filterFunction = getFilterFunction(filter);
if (filterFunction) {
replacement = filterFunction(replacement);
}
}
return replacement;
},
);
if (useDate && missingRequiredDate) {
const err = new Error();
err.name = SLUG_MISSING_REQUIRED_DATE;
throw err;
} else {
return compiledString;
}
}
export function extractTemplateVars(template: string) {
const regexp = RegExp(templateVariablePattern, 'g');
const contentRegexp = RegExp(templateContentPattern, 'g');
const matches = template.match(regexp) || [];
return matches.map(elem => {
const match = elem.match(contentRegexp);
return match ? match[0] : '';
});
}
/**
* Appends `dirname`, `filename` and `extension` to the provided `fields` map.
* @param entryPath
* @param fields
* @param folder - optionally include a folder that the dirname will be relative to.
* eg: `addFileTemplateFields('foo/bar/baz.ext', fields, 'foo')`
* will result in: `{ dirname: 'bar', filename: 'baz', extension: 'ext' }`
*/
export function addFileTemplateFields(entryPath: string, fields: Map<string, string>, folder = '') {
if (!entryPath) {
return fields;
}
const extension = extname(entryPath);
const filename = basename(entryPath, extension);
const dirnameExcludingFolder = dirname(entryPath).replace(new RegExp(`^(/?)${folder}/?`), '$1');
fields = fields.withMutations(map => {
map.set('dirname', dirnameExcludingFolder);
map.set('filename', filename);
map.set('extension', extension === '' ? extension : extension.slice(1));
});
return fields;
}

View File

@@ -0,0 +1,31 @@
import isNumber from 'lodash/isNumber';
import type { List } from 'immutable';
export function validateMinMax(
t: (key: string, options: unknown) => string,
fieldLabel: string,
value?: List<unknown>,
min?: number,
max?: number,
) {
function minMaxError(messageKey: string) {
return {
type: 'RANGE',
message: t(`editor.editorControlPane.widget.${messageKey}`, {
fieldLabel,
minCount: min,
maxCount: max,
count: min,
}),
};
}
if ([min, max, value?.size].every(isNumber) && (value!.size < min! || value!.size > max!)) {
return minMaxError(min === max ? 'rangeCountExact' : 'rangeCount');
} else if (isNumber(min) && min > 0 && value?.size && value.size < min) {
return minMaxError('rangeMin');
} else if (isNumber(max) && value?.size && value.size > max) {
return minMaxError('rangeMax');
}
}

View File

@@ -0,0 +1,3 @@
const { getConfig } = require('../../scripts/webpack.js');
module.exports = getConfig();