Merge branch 'releases/v4' of github.com:strapi/strapi into v4/media-library-cm

This commit is contained in:
soupette 2021-11-04 08:00:43 +01:00
commit 4d907c873c
85 changed files with 370 additions and 388 deletions

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_basic_simples",
"info": {
"name": "simple",
"displayName": "simple",
"icon": "ambulance",
"description": ""
},

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_blog_test_comos",
"info": {
"name": "test comp",
"displayName": "test comp",
"icon": "air-freshener",
"description": ""
},

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_default_apples",
"info": {
"name": "apple",
"displayName": "apple",
"icon": "apple-alt",
"description": ""
},

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_default_cars",
"info": {
"name": "car",
"displayName": "car",
"icon": "align-right"
},
"options": {},

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_closingperiods",
"info": {
"name": "closingperiod",
"displayName": "closingperiod",
"description": "",
"icon": "angry"
},

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_dishes",
"info": {
"name": "dish",
"displayName": "dish",
"description": "",
"icon": "address-book"
},

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_openingtimes",
"info": {
"name": "openingtimes",
"displayName": "openingtimes",
"description": "",
"icon": "calendar"
},

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_restaurantservices",
"info": {
"name": "restaurantservice",
"displayName": "restaurantservice",
"description": "",
"icon": "cannabis"
},

View File

@ -1,7 +1,7 @@
{
"collectionName": "components_default_temps",
"info": {
"name": "temp",
"displayName": "temp",
"icon": "adjust",
"description": ""
},

View File

@ -1,3 +1,14 @@
module.exports = () => {
return (ctx, next) => next();
'use strict';
/**
* `test-middleware` middleware.
*/
module.exports = (config, { strapi }) => {
// Add your own logic here.
return async (ctx, next) => {
strapi.log.info('In test-middleware middleware.');
await next();
};
};

View File

@ -57,11 +57,11 @@ async function getTemplateQuestion() {
}
/**
*
* @param {string|null} projectName - The name of the project
* @param {string|null} template - The template the project should use
* @returns Array of prompt question objects
*/
// TODO: re-enabled once the template have been migrated to V4
async function getPromptQuestions(projectName /*, template */) {
async function getPromptQuestions(projectName /*, template*/) {
return [
{
type: 'input',
@ -85,7 +85,7 @@ async function getPromptQuestions(projectName /*, template */) {
},
],
},
// TODO: re-enabled once the template have been migrated to V4
// TODO: re-enable once we know where to list the official compatible templates
// {
// type: 'confirm',
// name: 'useTemplate',

View File

@ -4,23 +4,18 @@ import styled from 'styled-components';
import { useIntl } from 'react-intl';
import PlusCircle from '@strapi/icons/PlusCircle';
import { Box } from '@strapi/design-system/Box';
import { BaseButton } from '@strapi/design-system/BaseButton';
import { Stack } from '@strapi/design-system/Stack';
import { Flex } from '@strapi/design-system/Flex';
import { Text } from '@strapi/design-system/Text';
import { pxToRem } from '@strapi/helper-plugin';
import { getTrad } from '../../utils';
const IconButton = styled(BaseButton)`
border: none;
padding: 0;
background: transparent;
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
const IconWrapper = styled.span`
> svg {
width: ${pxToRem(24)};
height: ${pxToRem(24)};
> circle {
fill: ${({ theme }) => theme.colors.primary200}!important;
fill: ${({ theme }) => theme.colors.primary200};
}
> path {
fill: ${({ theme }) => theme.colors.primary600};
@ -33,17 +28,21 @@ const ComponentInitializer = ({ isReadOnly, onClick }) => {
return (
<Box
as="button"
background="neutral100"
hasRadius
borderColor="neutral200"
disabled={isReadOnly}
hasRadius
onClick={onClick}
paddingTop={9}
paddingBottom={9}
type="button"
>
<Stack size={2}>
<Flex justifyContent="center" style={{ cursor: isReadOnly ? 'not-allowed' : 'inherit' }}>
<IconButton disabled={isReadOnly} onClick={onClick}>
<IconWrapper>
<PlusCircle />
</IconButton>
</IconWrapper>
</Flex>
<Flex justifyContent="center">
<Text textColor="primary600" small bold>

View File

@ -26,12 +26,12 @@ const Category = ({ category, components, isOdd, isOpen, onAddComponent, onToggl
<AccordionContent>
<Box paddingTop={4} paddingBottom={4} paddingLeft={3} paddingRight={3}>
<Grid>
{components.map(({ componentUid, info: { label, icon, name } }) => {
{components.map(({ componentUid, info: { displayName, icon } }) => {
return (
<ComponentCard
key={componentUid}
componentUid={componentUid}
intlLabel={{ id: label || name, defaultMessage: label || name }}
intlLabel={{ id: displayName, defaultMessage: displayName }}
icon={icon}
onClick={onAddComponent}
/>

View File

@ -18,7 +18,13 @@ import Image from '@strapi/icons/Picture';
import Link from '@strapi/icons/Link';
import Quote from '@strapi/icons/Quote';
import More from '@strapi/icons/More';
import { MainButtons, CustomIconButton, MoreButton, IconButtonGroupMargin } from './WysiwygStyles';
import {
MainButtons,
CustomIconButton,
MoreButton,
IconButtonGroupMargin,
CustomLinkIconButton,
} from './WysiwygStyles';
const WysiwygNav = ({
editorRef,
@ -75,7 +81,7 @@ const WysiwygNav = ({
/>
</MainButtons>
<MoreButton disabled ref={buttonMoreRef} id="more" label="more" icon={<More />} />
<MoreButton disabled ref={buttonMoreRef} id="more" label="More" icon={<More />} />
</Flex>
<Button onClick={onTogglePreviewMode} variant="tertiary" size="L" id="preview">
@ -135,7 +141,7 @@ const WysiwygNav = ({
ref={buttonMoreRef}
onClick={onTogglePopover}
id="more"
label="more"
label="More"
icon={<More />}
/>
{visiblePopover && (
@ -179,7 +185,7 @@ const WysiwygNav = ({
name="Image"
icon={<Image />}
/>
<CustomIconButton
<CustomLinkIconButton
onClick={() => onActionClick('Link', editorRef, onTogglePopover)}
id="Link"
label="Link"

View File

@ -21,6 +21,13 @@ export const CustomIconButton = styled(IconButton)`
}
`;
export const CustomLinkIconButton = styled(CustomIconButton)`
svg {
width: ${8 / 16}rem;
height: ${8 / 16}rem;
}
`;
export const MainButtons = styled(IconButtonGroup)`
margin-left: ${({ theme }) => theme.spaces[4]};
`;

View File

@ -73,7 +73,7 @@ const DynamicZoneList = ({ components }) => {
</CustomFlex>
<Box paddingTop={1}>
<Typography fontSize={1} textColor="neutral600" fontWeight="bold">
{get(componentLayouts, [componentUid, 'info', 'name'], '')}
{get(componentLayouts, [componentUid, 'info', 'displayName'], '')}
</Typography>
</Box>
</CustomLink>

View File

@ -45,7 +45,7 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, upd
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);
const { componentLayouts, initialData, modifiedData, metaToEdit, metaForm } = reducerState;
const { formatMessage } = useIntl();
const modelName = get(mainLayout, ['info', isContentTypeView ? 'displayName' : 'name'], '');
const modelName = get(mainLayout, ['info', 'displayName'], '');
const attributes = get(modifiedData, ['attributes'], {});
const entryTitleOptions = Object.keys(attributes).filter(attr => {

View File

@ -3,9 +3,9 @@ import PropTypes from 'prop-types';
import { Box } from '@strapi/design-system/Box';
import { Flex } from '@strapi/design-system/Flex';
import { Td, Tr } from '@strapi/design-system/Table';
import { Text } from '@strapi/design-system/Text';
import { Text, EllipsisText } from '@strapi/design-system/Text';
import { IconButton } from '@strapi/design-system/IconButton';
import { stopPropagation, onRowClick } from '@strapi/helper-plugin';
import { stopPropagation, onRowClick, pxToRem } from '@strapi/helper-plugin';
import { useIntl } from 'react-intl';
const RoleRow = ({ id, name, description, usersCount, icons }) => {
@ -26,11 +26,11 @@ const RoleRow = ({ id, name, description, usersCount, icons }) => {
fn: icons[1].onClick,
})}
>
<Td>
<Text textColor="neutral800">{name}</Text>
<Td maxWidth={pxToRem(130)}>
<EllipsisText textColor="neutral800">{name}</EllipsisText>
</Td>
<Td>
<Text textColor="neutral800">{description}</Text>
<Td maxWidth={pxToRem(250)}>
<EllipsisText textColor="neutral800">{description}</EllipsisText>
</Td>
<Td>
<Text textColor="neutral800">{usersCountText}</Text>

View File

@ -331,7 +331,7 @@ const ListView = () => {
onValueChange={handleSelectAllCheckbox}
/>
</Th>
<Th>
<Th width="20%">
<TableLabel textColor="neutral600">
{formatMessage({
id: 'Settings.webhooks.form.name',
@ -339,7 +339,7 @@ const ListView = () => {
})}
</TableLabel>
</Th>
<Th>
<Th width="60%">
<TableLabel textColor="neutral600">
{formatMessage({
id: 'Settings.webhooks.form.url',
@ -347,7 +347,7 @@ const ListView = () => {
})}
</TableLabel>
</Th>
<Th width="30%">
<Th width="20%">
<TableLabel textColor="neutral600">
{formatMessage({
id: 'Settings.webhooks.list.th.status',

View File

@ -14,7 +14,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -14,7 +14,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -16,7 +16,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -16,7 +16,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -16,7 +16,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -34,7 +34,7 @@ const productWithDP = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -14,7 +14,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -38,7 +38,7 @@ const models = {
},
},
simpleCompo: {
name: 'simple-compo',
displayName: 'simple-compo',
attributes: {
name: {
type: 'string',
@ -46,7 +46,7 @@ const models = {
},
},
otherCompo: {
name: 'compo-with-other-compo',
displayName: 'compo-with-other-compo',
attributes: {
compo: {
type: 'component',

View File

@ -23,7 +23,7 @@ const uploadImg = () => {
const components = {
singleMedia: {
name: 'single-media',
displayName: 'single-media',
attributes: {
media: {
type: 'media',
@ -31,7 +31,7 @@ const components = {
},
},
multipleMedia: {
name: 'multiple-media',
displayName: 'multiple-media',
attributes: {
media: {
type: 'media',
@ -40,7 +40,7 @@ const components = {
},
},
withNested: {
name: 'with-nested',
displayName: 'with-nested',
attributes: {
singleMedia: {
type: 'component',

View File

@ -83,7 +83,7 @@ const ComponentBox = styled(Box)`
function ComponentCard({ component, dzName, index, isActive, isInDevelopmentMode, onClick }) {
const { modifiedData, removeComponentFromDynamicZone } = useDataManager();
const {
schema: { icon, name },
schema: { icon, displayName },
} = get(modifiedData, ['components', component], {
schema: { icon: null },
});
@ -95,11 +95,16 @@ function ComponentCard({ component, dzName, index, isActive, isInDevelopmentMode
return (
<button type="button" onClick={onClick}>
<ComponentBox className={isActive ? 'active' : ''} borderRadius="borderRadius">
<ComponentBox
className={isActive ? 'active' : ''}
borderRadius="borderRadius"
paddingLeft={4}
paddingRight={4}
>
<Stack size={1} style={{ justifyContent: 'center', alignItems: 'center' }}>
<StyledFontAwesomeIcon icon={icon} />
<Text small bold>
{name}
<Text small bold ellipsis style={{ width: `calc(${pxToRem(140)} - 32px)` }}>
{displayName}
</Text>
</Stack>
{isInDevelopmentMode && (

View File

@ -44,7 +44,7 @@ const ContentTypeBuilderNav = () => {
return (
<React.Fragment key={section.name}>
<SubNavSection
label={formatMessage({ id: title, defaultMessage: title.defaultMessage })}
label={formatMessage({ id: title, defaultMessage: title })}
collapsable
badgeLabel={section.links.length.toString()}
>

View File

@ -1152,7 +1152,9 @@ exports[`<ContentTypeBuilderNav /> renders and matches the snapshot 1`] = `
</nav>
<div
class="c41 c42"
/>
>
<div />
</div>
</div>
<div
class="c43"

View File

@ -32,7 +32,9 @@ const makeApp = () => {
<LanguageProvider messages={messages} localeNames={localeNames}>
<Theme>
<Router history={history}>
<Layout sideNav={<ContentTypeBuilderNav />} />
<Layout sideNav={<ContentTypeBuilderNav />}>
<div />
</Layout>
</Router>
</Theme>
</LanguageProvider>

View File

@ -43,7 +43,7 @@ const useContentTypeBuilderMenu = () => {
componentsGroupedByCategory[category].map(compo => ({
name: compo.uid,
to: `/plugins/${pluginId}/component-categories/${category}/${compo.uid}`,
title: compo.schema.name,
title: compo.schema.displayName,
})),
obj => obj.title
),

View File

@ -545,13 +545,12 @@ const reducer = (state = initialState, action) =>
}
case actions.UPDATE_SCHEMA: {
const {
data: { name, collectionName, category, icon, kind },
data: { displayName, category, icon, kind },
schemaType,
uid,
} = action;
draftState.modifiedData[schemaType].schema.collectionName = collectionName;
draftState.modifiedData[schemaType].schema.name = name;
draftState.modifiedData[schemaType].schema.displayName = displayName;
if (action.schemaType === 'component') {
draftState.modifiedData.component.category = category;

View File

@ -715,8 +715,7 @@ describe('CTB | components | DataManagerProvider | reducer | basics actions ', (
describe('UPDATE_SCHEMA', () => {
it('Should update the modified data correctly if the schemaType is a content type', () => {
const data = {
name: 'test1',
collectionName: 'newTest',
displayName: 'test1',
};
const state = {
@ -726,8 +725,7 @@ describe('CTB | components | DataManagerProvider | reducer | basics actions ', (
contentType: {
uid: 'test',
schema: {
name: 'test',
collectionName: 'test',
displayName: 'test',
attributes: [
{
name: 'something',
@ -751,8 +749,7 @@ describe('CTB | components | DataManagerProvider | reducer | basics actions ', (
contentType: {
uid: 'test',
schema: {
name: 'test1',
collectionName: 'newTest',
displayName: 'test1',
attributes: [
{
name: 'something',
@ -769,8 +766,7 @@ describe('CTB | components | DataManagerProvider | reducer | basics actions ', (
it('Should update the modified data correctly if the schemaType is a component', () => {
const data = {
name: 'newTest',
collectionName: 'newTest',
displayName: 'newTest',
category: 'test',
icon: 'test',
};
@ -782,9 +778,8 @@ describe('CTB | components | DataManagerProvider | reducer | basics actions ', (
uid: 'test',
category: 'default',
schema: {
name: 'test',
displayName: 'test',
icon: 'book',
collectionName: 'components_tests',
attributes: [
{
name: 'something',
@ -800,9 +795,8 @@ describe('CTB | components | DataManagerProvider | reducer | basics actions ', (
uid: 'test',
category: 'default',
schema: {
name: 'test',
displayName: 'test',
icon: 'book',
collectionName: 'components_tests',
attributes: [
{
name: 'something',
@ -827,9 +821,8 @@ describe('CTB | components | DataManagerProvider | reducer | basics actions ', (
uid: 'test',
category: 'test',
schema: {
name: 'newTest',
displayName: 'newTest',
icon: 'test',
collectionName: 'newTest',
attributes: [
{
name: 'something',
@ -845,9 +838,8 @@ describe('CTB | components | DataManagerProvider | reducer | basics actions ', (
uid: 'test',
category: 'test',
schema: {
name: 'newTest',
displayName: 'newTest',
icon: 'test',
collectionName: 'newTest',
attributes: [
{
name: 'something',

View File

@ -7,7 +7,7 @@ import { CATEGORY_NAME_REGEX } from '../category';
const createComponentSchema = (usedComponentNames, reservedNames, category) => {
const shape = {
name: yup
displayName: yup
.string()
.test({
name: 'nameAlreadyUsed',

View File

@ -7,11 +7,11 @@ const componentForm = {
sectionTitle: null,
items: [
{
name: `${prefix}name`,
name: `${prefix}displayName`,
type: 'text',
intlLabel: {
id: getTrad('modalForm.attribute.form.base.name'),
defaultMessage: 'Name',
id: getTrad('contentType.displayName.label'),
defaultMessage: 'Display Name',
},
},
{

View File

@ -221,10 +221,9 @@ const FormModal = () => {
actionType,
modalType,
data: {
name: data.schema.name,
displayName: data.schema.displayName,
category: data.category,
icon: data.schema.icon,
collectionName: data.schema.collectionName,
},
});
}
@ -496,10 +495,11 @@ const FormModal = () => {
return;
}
} else if (isCreatingComponent) {
// We are creating a component using the component modal from the left menu
} else if (modalType === 'component') {
if (isCreating) {
// Create the component schema
const componentUid = createComponentUid(modifiedData.name, modifiedData.category);
const componentUid = createComponentUid(modifiedData.displayName, modifiedData.category);
const { category, ...rest } = modifiedData;
createSchema(rest, 'component', componentUid, category);
@ -652,7 +652,7 @@ const FormModal = () => {
const { category, type, ...rest } = componentToCreate;
// Create a the component temp UID
// This could be refactored but I think it's more understandable to separate the logic
const componentUid = createComponentUid(componentToCreate.name, category);
const componentUid = createComponentUid(componentToCreate.displayName, category);
// Create the component first and add it to the components data
createSchema(
// Component data
@ -686,7 +686,10 @@ const FormModal = () => {
if (isInFirstComponentStep) {
if (isCreatingComponentFromAView) {
const { category, type, ...rest } = modifiedData.componentToCreate;
const componentUid = createComponentUid(modifiedData.componentToCreate.name, category);
const componentUid = createComponentUid(
modifiedData.componentToCreate.displayName,
category
);
// Create the component first and add it to the components data
createSchema(
// Component data

View File

@ -189,11 +189,11 @@ const reducer = (state = initialState, action) =>
// This is run when the user has created a new component
const componentToCreate = state.modifiedData.componentToCreate;
const modifiedData = {
name: componentToCreate.name,
displayName: componentToCreate.displayName,
type: 'component',
repeatable: false,
...action.options,
component: createComponentUid(componentToCreate.name, componentToCreate.category),
component: createComponentUid(componentToCreate.displayName, componentToCreate.category),
};
const nextState = {

View File

@ -362,7 +362,7 @@ describe('CTB | components | FormModal | reducer | actions', () => {
createComponent: true,
componentToCreate: {
type: 'component',
name: 'compo',
displayName: 'compo',
icon: 'air-freshener',
category: 'default',
},
@ -373,12 +373,12 @@ describe('CTB | components | FormModal | reducer | actions', () => {
...initialState,
componentToCreate: {
type: 'component',
name: 'compo',
displayName: 'compo',
icon: 'air-freshener',
category: 'default',
},
modifiedData: {
name: 'compo',
displayName: 'compo',
type: 'component',
repeatable: false,
component: 'default.compo',

View File

@ -36,7 +36,7 @@ const FormModalHeader = ({
let headers = [];
const schema = modifiedData?.[forTarget]?.[targetUid] || modifiedData?.[forTarget] || null;
let displayName = forTarget === 'contentType' ? schema?.schema.displayName : schema?.schema.name;
let displayName = schema?.schema.displayName;
if (modalType === 'contentType') {
icon = contentTypeKind;
@ -78,7 +78,7 @@ const FormModalHeader = ({
headers = [
{
label: displayName,
info: { category: schema?.category || null, name: schema?.schema.name },
info: { category: schema?.category || null, name: schema?.schema.displayName },
},
];

View File

@ -39,7 +39,7 @@ const SelectComponent = ({
const compos = components.map(component => {
return {
uid: component.uid,
label: component.schema.name,
label: component.schema.displayName,
categoryName,
};
});
@ -61,7 +61,11 @@ const SelectComponent = ({
if (isCreatingComponentWhileAddingAField) {
options = [
{ uid: value, label: componentToCreate.name, categoryName: componentToCreate.category },
{
uid: value,
label: componentToCreate.displayName,
categoryName: componentToCreate.category,
},
];
}

View File

@ -36,8 +36,8 @@ const SelectComponents = ({ dynamicZoneTarget, intlLabel, name, onChange, value
const [categoryName, components] = current;
const section = {
label: categoryName,
children: components.map(({ uid, schema: { name } }) => {
return { label: name, value: uid };
children: components.map(({ uid, schema: { displayName } }) => {
return { label: displayName, value: uid };
}),
};

View File

@ -53,7 +53,6 @@ const ListView = () => {
const contentTypeKind = get(modifiedData, [firstMainDataPath, 'schema', 'kind'], null);
const attributes = get(modifiedData, mainDataTypeAttributesPath, []);
const currentDataName = get(initialData, [firstMainDataPath, 'schema', 'name'], '');
const isFromPlugin = has(initialData, [firstMainDataPath, 'plugin']);
const hasModelBeenModified = !isEqual(modifiedData, initialData);
@ -76,10 +75,7 @@ const ListView = () => {
});
};
// TODO: fixme
let label = isInContentTypeView
? get(modifiedData, [firstMainDataPath, 'schema', 'displayName'], '')
: get(modifiedData, [firstMainDataPath, 'schema', 'name'], '');
let label = get(modifiedData, [firstMainDataPath, 'schema', 'displayName'], '');
const kind = get(modifiedData, [firstMainDataPath, 'schema', 'kind'], '');
const isCreatingFirstContentType = match?.params.currentUID === 'create-content-type';
@ -192,9 +188,6 @@ const ListView = () => {
customRowComponent={props => <ListRow {...props} onClick={handleClickEditField} />}
addComponentToDZ={handleClickAddComponentToDZ}
targetUid={targetUid}
dataType={forTarget}
dataTypeName={currentDataName}
mainTypeName={currentDataName}
editTarget={forTarget}
isMain
/>

View File

@ -47,6 +47,8 @@ exports[`<ListView /> renders and matches the snapshot 1`] = `
}
.c63 {
padding-right: 16px;
padding-left: 16px;
border-radius: borderRadius;
}
@ -678,6 +680,10 @@ exports[`<ListView /> renders and matches the snapshot 1`] = `
font-size: 0.75rem;
line-height: 1.33;
color: #32324d;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.c33 {
@ -3418,9 +3424,8 @@ exports[`<ListView /> renders and matches the snapshot 1`] = `
</svg>
<span
class="c31 c67"
>
Compo
</span>
style="width: calc(8.75rem - 32px);"
/>
</div>
<div
class="c68 c69"
@ -3469,9 +3474,8 @@ exports[`<ListView /> renders and matches the snapshot 1`] = `
</svg>
<span
class="c31 c67"
>
dish
</span>
style="width: calc(8.75rem - 32px);"
/>
</div>
<div
class="c68 c69"
@ -3520,9 +3524,8 @@ exports[`<ListView /> renders and matches the snapshot 1`] = `
</svg>
<span
class="c31 c67"
>
openingtimes
</span>
style="width: calc(8.75rem - 32px);"
/>
</div>
<div
class="c68 c69"
@ -3571,9 +3574,8 @@ exports[`<ListView /> renders and matches the snapshot 1`] = `
</svg>
<span
class="c31 c67"
>
restaurantservice
</span>
style="width: calc(8.75rem - 32px);"
/>
</div>
<div
class="c68 c69"

View File

@ -27,7 +27,7 @@ describe('Component validator', () => {
components: [],
component: {
category: 'default',
name: 'mycompo',
displayName: 'mycompo',
icon: 'american-sign-language-interpreting',
attributes: {
title: {
@ -49,7 +49,7 @@ describe('Component validator', () => {
components: [],
component: {
category: 'default',
name: 'mycompo',
displayName: 'mycompo',
icon: 'american-sign-language-interpreting',
attributes: {
title: {
@ -73,7 +73,7 @@ describe('Component validator', () => {
components: [],
component: {
category: 'default',
name: 'mycompo',
displayName: 'mycompo',
icon: 'american-sign-language-interpreting',
attributes: {
title: {

View File

@ -44,7 +44,7 @@ describe('Content type validator', () => {
test('Throws when reserved names are used', async () => {
const data = {
contentType: {
name: 'test',
displayName: 'test',
attributes: {
thisIsReserved: {
type: 'string',
@ -133,7 +133,7 @@ describe('Content type validator', () => {
{
uid: 'edit',
icon: 'star',
name: 'test',
displayName: 'test',
category: 'test',
attributes: {
title: {
@ -145,7 +145,7 @@ describe('Content type validator', () => {
{
tmpUID: 'random',
icon: 'star',
name: 'test2',
displayName: 'test2',
category: 'test',
attributes: {
title: {

View File

@ -16,10 +16,10 @@ const componentSchema = createSchema(VALID_TYPES, VALID_RELATIONS, {
modelType: modelTypes.COMPONENT,
})
.shape({
name: yup
displayName: yup
.string()
.min(1)
.required('name.required'),
.required('displayName.required'),
icon: yup
.string()
.nullable()

View File

@ -1,11 +1,22 @@
'use strict';
module.exports = () => ({
// TODO: Implement
getReservedNames() {
return {
models: [],
attributes: [],
models: ['boolean', 'date', 'date-time', 'dateTime', 'time', 'upload'],
attributes: [
'id',
'created_at',
'createdAt',
'updated_at',
'updatedAt',
'created_by',
'createdBy',
'updated_by',
'updatedBy',
'published_at',
'publishedAt',
],
};
// strapi.db.getReservedNames();
},

View File

@ -1,7 +1,6 @@
'use strict';
const _ = require('lodash');
const pluralize = require('pluralize');
const { formatAttributes, replaceTemporaryUIDs } = require('../utils/attributes');
const createBuilder = require('./schema-builder');
@ -20,7 +19,7 @@ const formatComponent = component => {
apiId: modelName,
schema: {
icon: _.get(info, 'icon'),
name: _.get(info, 'name') || _.upperFirst(pluralize(uid)),
displayName: _.get(info, 'displayName'),
description: _.get(info, 'description', ''),
connection,
collectionName,

View File

@ -14,10 +14,10 @@ module.exports = function createComponentBuilder() {
* Returns a uid from a component infos
* @param {Object} options options
* @param {string} options.category component category
* @param {string} options.name component name
* @param {string} options.displayName component displayName
*/
createComponentUID({ category, name }) {
return `${nameToSlug(category)}.${nameToSlug(name)}`;
createComponentUID({ category, displayName }) {
return `${nameToSlug(category)}.${nameToSlug(displayName)}`;
},
createNewComponentUIDMap(components) {
@ -39,17 +39,17 @@ module.exports = function createComponentBuilder() {
const handler = createSchemaHandler({
dir: path.join(strapi.dirs.components, nameToSlug(infos.category)),
filename: `${nameToSlug(infos.name)}.json`,
filename: `${nameToSlug(infos.displayName)}.json`,
});
const collectionName = `components_${nameToCollectionName(
infos.category
)}_${nameToCollectionName(pluralize(infos.name))}`;
)}_${nameToCollectionName(pluralize(infos.displayName))}`;
handler
.setUID(uid)
.set('collectionName', collectionName)
.set(['info', 'name'], infos.name)
.set(['info', 'displayName'], infos.displayName)
.set(['info', 'icon'], infos.icon)
.set(['info', 'description'], infos.description)
.set('pluginOptions', infos.pluginOptions)
@ -98,7 +98,7 @@ module.exports = function createComponentBuilder() {
component
.setUID(newUID)
.setDir(newDir)
.set(['info', 'name'], infos.name)
.set(['info', 'displayName'], infos.displayName)
.set(['info', 'icon'], infos.icon)
.set(['info', 'description'], infos.description)
.set('pluginOptions', infos.pluginOptions)

View File

@ -38,7 +38,7 @@ describe('Content Type Builder - Components', () => {
'component.category': ['category.required'],
'component.icon': ['icon.required'],
'component.attributes': ['attributes.required'],
'component.name': ['name.required'],
'component.displayName': ['displayName.required'],
},
});
});
@ -51,7 +51,7 @@ describe('Content Type Builder - Components', () => {
component: {
category: 'default',
icon: 'default',
name: 'Some Component',
displayName: 'Some Component',
pluginOptions: {
pluginName: {
option: true,
@ -92,7 +92,7 @@ describe('Content Type Builder - Components', () => {
component: {
category: 'default',
icon: 'default',
name: 'someComponent',
displayName: 'someComponent',
attributes: {},
},
},
@ -121,7 +121,7 @@ describe('Content Type Builder - Components', () => {
expect(el).toMatchObject({
uid: expect.any(String),
schema: expect.objectContaining({
name: expect.any(String),
displayName: expect.any(String),
description: expect.any(String),
collectionName: expect.any(String),
attributes: expect.objectContaining({}),
@ -157,7 +157,7 @@ describe('Content Type Builder - Components', () => {
category: 'default',
schema: {
icon: 'default',
name: 'Some Component',
displayName: 'Some Component',
description: '',
collectionName: 'components_default_some_components',
pluginOptions: {
@ -215,7 +215,7 @@ describe('Content Type Builder - Components', () => {
error: {
'component.category': ['category.required'],
'component.icon': ['icon.required'],
'component.name': ['name.required'],
'component.displayName': ['displayName.required'],
},
});
});
@ -228,7 +228,7 @@ describe('Content Type Builder - Components', () => {
component: {
category: 'default',
icon: 'default',
name: 'New Component',
displayName: 'New Component',
attributes: {
name: {
type: 'string',
@ -262,7 +262,7 @@ describe('Content Type Builder - Components', () => {
data: {
uid: 'default.some-component',
schema: {
name: 'New Component',
displayName: 'New Component',
pluginOptions: {
pluginName: {
option: false,

View File

@ -127,7 +127,7 @@ program
// `$ strapi generate:template <directory>`
program
.command('generate:template <directory>')
.command('templates:generate <directory>')
.description('Generate template from Strapi project')
.action(getLocalScript('generate-template'));

View File

@ -30,23 +30,20 @@ describe('generate:template command', () => {
expect(fse.ensureDir).toHaveBeenCalledWith(templatePath);
});
it.each(['api', 'components', 'config/functions/bootstrap.js', 'data'])(
'copies folder %s',
async item => {
// Mock the empty directory arg
fse.pathExists.mockReturnValueOnce(false);
// Mock the folder exists
fse.pathExists.mockReturnValue(true);
const directory = '../test-dir';
const rootPath = resolve(directory);
const templatePath = join(rootPath, 'template');
it.each(['src', 'data'])('copies folder %s', async item => {
// Mock the empty directory arg
fse.pathExists.mockReturnValueOnce(false);
// Mock the folder exists
fse.pathExists.mockReturnValue(true);
const directory = '../test-dir';
const rootPath = resolve(directory);
const templatePath = join(rootPath, 'template');
await exportTemplate(directory);
await exportTemplate(directory);
expect(fse.pathExists).toHaveBeenCalledWith(join(process.cwd(), item));
expect(fse.copy).toHaveBeenCalledWith(join(process.cwd(), item), join(templatePath, item));
}
);
expect(fse.pathExists).toHaveBeenCalledWith(join(process.cwd(), item));
expect(fse.copy).toHaveBeenCalledWith(join(process.cwd(), item), join(templatePath, item));
});
it('creates a json config file', async () => {
fse.pathExists.mockReturnValue(false);

View File

@ -6,7 +6,7 @@ const chalk = require('chalk');
const inquirer = require('inquirer');
// All directories that a template could need
const TEMPLATE_CONTENT = ['api', 'components', 'config/functions/bootstrap.js', 'data'];
const TEMPLATE_CONTENT = ['src', 'data'];
/**
*
@ -54,10 +54,9 @@ async function writeTemplateJson(rootPath) {
* @returns boolean
*/
async function templateConfigExists(rootPath) {
const jsonConfig = await fse.pathExists(join(rootPath, 'template.json'));
const functionConfig = await fse.pathExists(join(rootPath, 'template.js'));
return jsonConfig || functionConfig;
const configExists = await fse.pathExists(join(rootPath, 'template.json'));
console.log(`checking: ${join(rootPath, 'template.json')}. result ${configExists}`);
return configExists;
}
module.exports = async function generateTemplate(directory) {

View File

@ -14,7 +14,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -14,7 +14,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -14,7 +14,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -14,7 +14,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -14,7 +14,7 @@ let data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -14,7 +14,7 @@ const data = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -28,7 +28,7 @@ const product = {
};
const compo = {
name: 'compo',
displayName: 'compo',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -8,7 +8,7 @@ let strapi;
let rq;
const component = {
name: 'somecomponent',
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',

View File

@ -38,7 +38,7 @@ const models = {
},
},
simpleCompo: {
name: 'simple-compo',
displayName: 'simple-compo',
attributes: {
name: {
type: 'string',
@ -46,7 +46,7 @@ const models = {
},
},
otherCompo: {
name: 'compo-with-other-compo',
displayName: 'compo-with-other-compo',
attributes: {
compo: {
type: 'component',

View File

@ -23,7 +23,7 @@ const uploadImg = () => {
const components = {
singleMedia: {
name: 'one-media',
displayName: 'one-media',
attributes: {
media: {
type: 'media',
@ -31,7 +31,7 @@ const components = {
},
},
multipleMedia: {
name: 'many-media',
displayName: 'many-media',
attributes: {
media: {
type: 'media',
@ -40,7 +40,7 @@ const components = {
},
},
withNested: {
name: 'with-nested',
displayName: 'with-nested',
attributes: {
singleMedia: {
type: 'component',

View File

@ -97,7 +97,7 @@ const contentTypes = {
const components = {
comp: {
name: 'comp',
displayName: 'comp',
attributes: {
countries: {
type: 'relation',

View File

@ -18,19 +18,7 @@ sentry.init({
dsn: 'https://841d2b2c9b4d4b43a4cde92794cb705a@sentry.io/1762059',
});
// TODO: to remove after the templates are updated for V4
const warnTemplatesAreDisabled = cliArguments => {
if (cliArguments.template) {
console.log(
'❌ Templates have been disabled for the Beta version of V4 and will be re-enabled soon!'
);
process.exit();
}
};
module.exports = (projectDirectory, cliArguments) => {
warnTemplatesAreDisabled(cliArguments);
checkRequirements();
const rootPath = resolve(projectDirectory);

View File

@ -1,94 +0,0 @@
'use strict';
const fetch = require('node-fetch');
const tar = require('tar');
const parseGitUrl = require('git-url-parse');
const chalk = require('chalk');
const stopProcess = require('./stop-process');
function parseShorthand(template) {
// Determine if it is comes from another owner
if (template.includes('/')) {
const [owner, partialName] = template.split('/');
const name = `strapi-template-${partialName}`;
return {
name,
fullName: `${owner}/${name}`,
};
}
const name = `strapi-template-${template}`;
return {
name,
fullName: `strapi/${name}`,
};
}
/**
* @param {string} repo The full name of the repository.
*/
async function getDefaultBranch(repo) {
const response = await fetch(`https://api.github.com/repos/${repo}`);
if (!response.ok) {
stopProcess(
`Could not find the information for ${chalk.yellow(
repo
)}. Make sure it is publicly accessible on github.`
);
}
const { default_branch } = await response.json();
return default_branch;
}
/**
* @param {string} template GitHub URL or shorthand to a template project.
*/
async function getRepoInfo(template) {
const { name, full_name: fullName, ref, filepath, protocols, source } = parseGitUrl(template);
if (protocols.length === 0) {
const repoInfo = parseShorthand(template);
return {
...repoInfo,
branch: await getDefaultBranch(repoInfo.fullName),
usedShorthand: true,
};
}
if (source !== 'github.com') {
stopProcess(`GitHub URL not found for: ${chalk.yellow(template)}.`);
}
let branch;
if (ref) {
// Append the filepath to the parsed ref since a branch name could contain '/'
// If so, the rest of the branch name will be considered 'filepath' by 'parseGitUrl'
branch = filepath ? `${ref}/${filepath}` : ref;
} else {
branch = await getDefaultBranch(fullName);
}
return { name, fullName, branch };
}
/**
* @param {string} repoInfo GitHub repository information (full name, branch...).
* @param {string} tmpDir Path to the destination temporary directory.
*/
async function downloadGitHubRepo(repoInfo, tmpDir) {
// Download from GitHub
const { fullName, branch } = repoInfo;
const codeload = `https://codeload.github.com/${fullName}/tar.gz/${branch}`;
const response = await fetch(codeload);
if (!response.ok) {
throw Error(`Could not download the ${chalk.yellow(fullName)} repository.`);
}
await new Promise(resolve => {
response.body.pipe(tar.extract({ strip: 1, cwd: tmpDir })).on('close', resolve);
});
}
module.exports = { getRepoInfo, downloadGitHubRepo };

View File

@ -0,0 +1,61 @@
'use strict';
const path = require('path');
const execa = require('execa');
const chalk = require('chalk');
/**
* Gets the package version on npm. Will fail if the package does not exist
* @param {string} packageName - Name to look up on npm, may include a specific version
* @returns {Object}
*/
async function getPackageInfo(packageName) {
const { stdout } = await execa.shell(`npm view ${packageName} name version --silent`);
// Use regex to parse name and version from CLI result
const [name, version] = stdout.match(/(?<=')(.*?)(?=')/gm);
return { name, version };
}
/**
* @param {string} template - The name of the template as provided by the user.
* @returns {Object} - The full name of the template package's name on npm
*/
async function getTemplatePackageInfo(template) {
// Check if template is a shorthand
try {
const longhand = `@strapi/template-${template}`;
const packageInfo = await getPackageInfo(longhand);
// Hasn't crashed so it is indeed a shorthand
return packageInfo;
} catch (error) {
// Ignore error, we now know it's not a shorthand
}
// Fetch version of the non-shorthand package
try {
return getPackageInfo(template);
} catch (error) {
throw new Error(`Could not find package ${chalk.green('template.json')} on npm`);
}
}
/**
* @param {Object} packageInfo - Template's npm package information
* @param {string} packageInfo.name
* @param {string} packageInfo.version
* @param {string} parentDir - Path inside of which we install the template.
*/
async function downloadNpmTemplate({ name, version }, parentDir) {
// Download from npm
await execa.shell(`npm install ${name}@${version} --no-save --silent`, {
cwd: parentDir,
});
// Return the path of the actual template
const exactTemplatePath = require.resolve(path.join('node_modules', name), {
paths: [parentDir],
});
// const exactTemplatePath = path.resolve(parentDir, 'node_modules', name);
return exactTemplatePath;
}
module.exports = { getTemplatePackageInfo, downloadNpmTemplate };

View File

@ -3,99 +3,83 @@
const os = require('os');
const path = require('path');
const fse = require('fs-extra');
const _ = require('lodash');
const _ = require('lodash/fp');
const chalk = require('chalk');
const { getRepoInfo, downloadGitHubRepo } = require('./fetch-github');
const { getTemplatePackageInfo, downloadNpmTemplate } = require('./fetch-npm-template');
// Specify all the files and directories a template can have
const allowChildren = '*';
const allowFile = Symbol();
const allowChildren = Symbol();
const allowedTemplateContents = {
'README.md': true,
'.env.example': true,
src: {
api: allowChildren,
components: allowChildren,
plugins: allowChildren,
},
config: {
functions: allowChildren,
},
'README.md': allowFile,
'.env.example': allowFile,
'package.json': allowFile,
src: allowChildren,
data: allowChildren,
database: allowChildren,
public: allowChildren,
scripts: allowChildren,
};
/**
* merge template with new project being created
* Merge template with new project being created
* @param {string} scope project creation params
* @param {string} rootPath project path
*/
module.exports = async function mergeTemplate(scope, rootPath) {
// Parse template info
const repoInfo = await getRepoInfo(scope.template);
const { fullName } = repoInfo;
console.log(`Installing ${chalk.yellow(fullName)} template.`);
let templatePath;
let templateParentPath;
let templatePackageInfo = {};
const isLocalTemplate = ['./', '../', '/'].some(filePrefix =>
scope.template.startsWith(filePrefix)
);
// Download template repository to a temporary directory
const templatePath = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-'));
await downloadGitHubRepo(repoInfo, templatePath);
if (isLocalTemplate) {
// Template is a local directory
console.log('Installing local template.');
templatePath = path.resolve(rootPath, '..', scope.template);
} else {
// Template should be an npm package. Fetch template info
templatePackageInfo = await getTemplatePackageInfo(scope.template);
console.log(`Installing ${chalk.yellow(templatePackageInfo.name)} template.`);
// Download template repository to a temporary directory
templateParentPath = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-'));
templatePath = await downloadNpmTemplate(templatePackageInfo, templateParentPath);
}
// Make sure the downloaded template matches the required format
const { templateConfig } = await checkTemplateRootStructure(templatePath, scope);
const templateConfig = await checkTemplateRootStructure(templatePath, scope);
await checkTemplateContentsStructure(path.resolve(templatePath, 'template'));
// Merge contents of the template in the project
const fullTemplateUrl = `https://github.com/${fullName}`;
await mergePackageJSON(rootPath, templateConfig, fullTemplateUrl);
await mergePackageJSON({ rootPath, templateConfig, templatePackageInfo });
await mergeFilesAndDirectories(rootPath, templatePath);
// Delete the downloaded template repo
await fse.remove(templatePath);
// Delete the template directory if it was downloaded
if (!isLocalTemplate) {
await fse.remove(templateParentPath);
}
};
// Make sure the template has the required top-level structure
async function checkTemplateRootStructure(templatePath, scope) {
// Make sure the root of the repo has a template.json or a template.js file
/**
* Make sure the template has the required top-level structure
* @param {string} templatePath - Path of the locally downloaded template
* @returns {Object} - The template config object
*/
async function checkTemplateRootStructure(templatePath) {
// Make sure the root of the repo has a template.json file
const templateJsonPath = path.join(templatePath, 'template.json');
const templateFunctionPath = path.join(templatePath, 'template.js');
// Store the template config, whether it comes from a JSON or a function
let templateConfig = {};
const hasJsonConfig = fse.existsSync(templateJsonPath);
if (hasJsonConfig) {
const jsonStat = await fse.stat(templateJsonPath);
if (!jsonStat.isFile()) {
throw new Error(`A template's ${chalk.green('template.json')} must be a file`);
}
templateConfig = require(templateJsonPath);
const templateJsonExists = await fse.exists(templateJsonPath);
if (!templateJsonExists) {
throw new Error(`A template must have a ${chalk.green('template.json')} root file`);
}
const templateJsonStat = await fse.stat(templateJsonPath);
if (!templateJsonStat.isFile()) {
throw new Error(`A template's ${chalk.green('template.json')} must be a file`);
}
const hasFunctionConfig = fse.existsSync(templateFunctionPath);
if (hasFunctionConfig) {
const functionStat = await fse.stat(templateFunctionPath);
if (!functionStat.isFile()) {
throw new Error(`A template's ${chalk.green('template.js')} must be a file`);
}
// Get the config by passing the scope to the function
templateConfig = require(templateFunctionPath)(scope);
}
// Make sure there's exactly one template config file
if (!hasJsonConfig && !hasFunctionConfig) {
throw new Error(
`A template must have either a ${chalk.green('template.json')} or a ${chalk.green(
'template.js'
)} root file`
);
} else if (hasJsonConfig && hasFunctionConfig) {
throw new Error(
`A template cannot have both ${chalk.green('template.json')} and ${chalk.green(
'template.js'
)} root files`
);
}
const templateConfig = require(templateJsonPath);
// Make sure the root of the repo has a template folder
const templateDirPath = path.join(templatePath, 'template');
@ -112,21 +96,24 @@ async function checkTemplateRootStructure(templatePath, scope) {
throw error;
}
return { templateConfig };
return templateConfig;
}
// Traverse template tree to make sure each file and folder is allowed
/**
* Traverse template tree to make sure each file and folder is allowed
* @param {string} templateContentsPath
*/
async function checkTemplateContentsStructure(templateContentsPath) {
// Recursively check if each item in a directory is allowed
const checkPathContents = (pathToCheck, parents) => {
const contents = fse.readdirSync(pathToCheck);
contents.forEach(item => {
const checkPathContents = async (pathToCheck, parents) => {
const contents = await fse.readdir(pathToCheck);
for (const item of contents) {
const nextParents = [...parents, item];
const matchingTreeValue = _.get(allowedTemplateContents, nextParents);
const matchingTreeValue = _.get(nextParents, allowedTemplateContents);
// Treat files and directories separately
const itemPath = path.resolve(pathToCheck, item);
const isDirectory = fse.statSync(itemPath).isDirectory();
const isDirectory = (await fse.stat(itemPath)).isDirectory();
if (matchingTreeValue === undefined) {
// Unknown paths are forbidden
@ -135,7 +122,7 @@ async function checkTemplateContentsStructure(templateContentsPath) {
);
}
if (matchingTreeValue === true) {
if (matchingTreeValue === allowFile) {
if (!isDirectory) {
// All good, the file is allowed
return;
@ -153,20 +140,28 @@ async function checkTemplateContentsStructure(templateContentsPath) {
return;
}
// Check if the contents of the directory are allowed
checkPathContents(itemPath, nextParents);
await checkPathContents(itemPath, nextParents);
} else {
throw Error(
`Illegal template structure, unknow file ${chalk.green(nextParents.join('/'))}`
);
}
});
}
};
checkPathContents(templateContentsPath, []);
await checkPathContents(templateContentsPath, []);
}
// Merge the template's template.json into the Strapi project's package.json
async function mergePackageJSON(rootPath, templateConfig, templateUrl) {
/**
* Merge the template's template.json into the Strapi project's package.json
* @param {Object} config
* @param {string} config.rootPath
* @param {string} config.templateConfig
* @param {Object} config.templatePackageInfo - Info about the template's package on npm
* @param {Object} config.templatePackageInfo.name - The name of the template's package on npm
* @param {Object} config.templatePackageInfo.version - The name of the template's package on npm
*/
async function mergePackageJSON({ rootPath, templateConfig, templatePackageInfo }) {
// Import the package.json as an object
const packageJSON = require(path.resolve(rootPath, 'package.json'));
@ -181,10 +176,12 @@ async function mergePackageJSON(rootPath, templateConfig, templateUrl) {
}
// Use lodash to deeply merge them
const mergedConfig = _.merge(packageJSON, templateConfig.package);
const mergedConfig = _.merge(templateConfig.package, packageJSON);
// Add starter info to package.json
_.set(mergedConfig, 'strapi.template', templateUrl);
// Add template info to package.json
if (templatePackageInfo.name) {
_.set('strapi.template', templatePackageInfo.name, mergedConfig);
}
// Save the merged config as the new package.json
const packageJSONPath = path.join(rootPath, 'package.json');

View File

@ -17,7 +17,6 @@
"chalk": "^4.1.1",
"execa": "^1.0.0",
"fs-extra": "^9.1.0",
"git-url-parse": "^11.4.4",
"inquirer": "^6.3.1",
"lodash": "4.17.21",
"node-fetch": "^2.6.1",

View File

@ -4,7 +4,7 @@
* `{{ name }}` middleware.
*/
module.exports = async (config, { strapi }) => {
module.exports = (config, { strapi }) => {
// Add your own logic here.
return async (ctx, next) => {
strapi.log.info('In {{ name }} middleware.');

View File

@ -29,7 +29,7 @@ const rgbColorComponent = {
type: 'integer',
},
},
name: 'rgbColor',
displayName: 'rgbColor',
};
const documentModel = {

View File

@ -11883,7 +11883,7 @@ git-url-parse@11.4.4:
dependencies:
git-up "^4.0.0"
git-url-parse@^11.1.2, git-url-parse@^11.4.4:
git-url-parse@^11.1.2:
version "11.6.0"
resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605"
integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g==