@strapi/generators 5.54.0 → 5.55.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/dist/plops/content-type.d.ts.map +1 -1
- package/dist/plops/content-type.js +41 -11
- package/dist/plops/content-type.js.map +1 -1
- package/dist/plops/content-type.mjs +40 -11
- package/dist/plops/content-type.mjs.map +1 -1
- package/dist/plops/prompts/get-folder-prompts.d.ts +14 -0
- package/dist/plops/prompts/get-folder-prompts.d.ts.map +1 -0
- package/dist/plops/prompts/get-folder-prompts.js +105 -0
- package/dist/plops/prompts/get-folder-prompts.js.map +1 -0
- package/dist/plops/prompts/get-folder-prompts.mjs +103 -0
- package/dist/plops/prompts/get-folder-prompts.mjs.map +1 -0
- package/dist/plops/utils/content-structure.d.ts +42 -0
- package/dist/plops/utils/content-structure.d.ts.map +1 -0
- package/dist/plops/utils/content-structure.js +260 -0
- package/dist/plops/utils/content-structure.js.map +1 -0
- package/dist/plops/utils/content-structure.mjs +247 -0
- package/dist/plops/utils/content-structure.mjs.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var contentStructure = require('../utils/content-structure.js');
|
|
4
|
+
|
|
5
|
+
const NEW_FOLDER = {
|
|
6
|
+
kind: 'new'
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Asks where to place the generated content type in the content-structure folder tree.
|
|
10
|
+
*/ const getFolderPrompts = async (inquirer, plop, { kind, destination })=>{
|
|
11
|
+
// A plugin content type only loads once the plugin is enabled in the app config.
|
|
12
|
+
// Since it can't be determined in this context whether a plugin is enabled or not,
|
|
13
|
+
// a folder assignment could be pruned on the next write to groups.json boot if its
|
|
14
|
+
// referenced content type is found not to exist. I've opted to sidestep this by
|
|
15
|
+
// disabling folder assignment for plugin content-types for the time being.
|
|
16
|
+
if (destination === 'plugin') {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
const read = contentStructure.readContentStructureFile(plop.getDestBasePath());
|
|
20
|
+
if (read.status === 'invalid') {
|
|
21
|
+
console.warn('The content-structure file (src/content-structure/groups.json) could not be read. Skipping folder assignment.');
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
const sectionKey = contentStructure.sectionKeyForKind(kind);
|
|
25
|
+
const groups = read.status === 'ok' ? contentStructure.getSectionGroups(read.file, sectionKey) : [];
|
|
26
|
+
if (groups === null) {
|
|
27
|
+
console.warn(`The "${sectionKey}" section of src/content-structure/groups.json is malformed. Skipping folder assignment.`);
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
const { addToFolder } = await inquirer.prompt([
|
|
31
|
+
{
|
|
32
|
+
message: 'Add this content type to a folder?',
|
|
33
|
+
name: 'addToFolder',
|
|
34
|
+
type: 'confirm',
|
|
35
|
+
default: false
|
|
36
|
+
}
|
|
37
|
+
]);
|
|
38
|
+
if (!addToFolder) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
const folderChoices = contentStructure.listFolderChoices(groups);
|
|
42
|
+
const folderChoicesOptions = folderChoices.map((choice)=>({
|
|
43
|
+
value: {
|
|
44
|
+
kind: 'existing',
|
|
45
|
+
id: choice.value
|
|
46
|
+
},
|
|
47
|
+
name: choice.name
|
|
48
|
+
}));
|
|
49
|
+
if (folderChoicesOptions.length > 0) {
|
|
50
|
+
const { folderChoice } = await inquirer.prompt([
|
|
51
|
+
{
|
|
52
|
+
message: 'Select a folder',
|
|
53
|
+
name: 'folderChoice',
|
|
54
|
+
type: 'list',
|
|
55
|
+
default: 0,
|
|
56
|
+
choices: [
|
|
57
|
+
{
|
|
58
|
+
name: 'Create a new folder',
|
|
59
|
+
value: NEW_FOLDER
|
|
60
|
+
},
|
|
61
|
+
...folderChoicesOptions
|
|
62
|
+
],
|
|
63
|
+
pageSize: folderChoicesOptions.length + 1
|
|
64
|
+
}
|
|
65
|
+
]);
|
|
66
|
+
if (folderChoice.kind === 'existing') {
|
|
67
|
+
return {
|
|
68
|
+
folder: {
|
|
69
|
+
targetGroupId: folderChoice.id
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const { folderName } = await inquirer.prompt([
|
|
75
|
+
{
|
|
76
|
+
message: 'Name of the new folder',
|
|
77
|
+
name: 'folderName',
|
|
78
|
+
type: 'input',
|
|
79
|
+
validate (input) {
|
|
80
|
+
if (typeof input !== 'string' || input.trim().length === 0) {
|
|
81
|
+
return 'The folder name cannot be empty';
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
]);
|
|
87
|
+
const trimmedName = folderName.trim();
|
|
88
|
+
// A new folder whose name matches an existing root folder reuses that folder instead of creating a duplicate.
|
|
89
|
+
const existingRootFolder = contentStructure.findRootFolderByName(groups, trimmedName);
|
|
90
|
+
if (existingRootFolder) {
|
|
91
|
+
return {
|
|
92
|
+
folder: {
|
|
93
|
+
targetGroupId: existingRootFolder.id
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
folder: {
|
|
99
|
+
newFolderName: trimmedName
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
module.exports = getFolderPrompts;
|
|
105
|
+
//# sourceMappingURL=get-folder-prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-folder-prompts.js","sources":["../../../src/plops/prompts/get-folder-prompts.ts"],"sourcesContent":["import type { NodePlopAPI } from 'plop';\n\nimport {\n readContentStructureFile,\n findRootFolderByName,\n sectionKeyForKind,\n listFolderChoices,\n getSectionGroups,\n type FolderSelection,\n} from '../utils/content-structure';\n\ntype FolderChoiceValue = { kind: 'new' } | { kind: 'existing'; id: string };\n\nconst NEW_FOLDER: FolderChoiceValue = { kind: 'new' };\n\ninterface FolderPromptContext {\n destination?: string;\n kind: string;\n}\n\n/**\n * Asks where to place the generated content type in the content-structure folder tree.\n */\nconst getFolderPrompts = async (\n inquirer: any,\n plop: NodePlopAPI,\n { kind, destination }: FolderPromptContext\n): Promise<{ folder?: FolderSelection }> => {\n // A plugin content type only loads once the plugin is enabled in the app config.\n // Since it can't be determined in this context whether a plugin is enabled or not,\n // a folder assignment could be pruned on the next write to groups.json boot if its\n // referenced content type is found not to exist. I've opted to sidestep this by\n // disabling folder assignment for plugin content-types for the time being.\n\n if (destination === 'plugin') {\n return {};\n }\n\n const read = readContentStructureFile(plop.getDestBasePath());\n\n if (read.status === 'invalid') {\n console.warn(\n 'The content-structure file (src/content-structure/groups.json) could not be read. Skipping folder assignment.'\n );\n\n return {};\n }\n\n const sectionKey = sectionKeyForKind(kind);\n const groups = read.status === 'ok' ? getSectionGroups(read.file, sectionKey) : [];\n\n if (groups === null) {\n console.warn(\n `The \"${sectionKey}\" section of src/content-structure/groups.json is malformed. Skipping folder assignment.`\n );\n\n return {};\n }\n\n const { addToFolder } = await inquirer.prompt([\n {\n message: 'Add this content type to a folder?',\n name: 'addToFolder',\n type: 'confirm',\n default: false,\n },\n ]);\n\n if (!addToFolder) {\n return {};\n }\n\n const folderChoices = listFolderChoices(groups);\n\n const folderChoicesOptions = folderChoices.map((choice) => ({\n value: { kind: 'existing', id: choice.value },\n name: choice.name,\n }));\n\n if (folderChoicesOptions.length > 0) {\n const { folderChoice } = await inquirer.prompt([\n {\n message: 'Select a folder',\n name: 'folderChoice',\n type: 'list',\n default: 0,\n choices: [{ name: 'Create a new folder', value: NEW_FOLDER }, ...folderChoicesOptions],\n pageSize: folderChoicesOptions.length + 1,\n },\n ]);\n\n if (folderChoice.kind === 'existing') {\n return { folder: { targetGroupId: folderChoice.id } };\n }\n }\n\n const { folderName } = await inquirer.prompt([\n {\n message: 'Name of the new folder',\n name: 'folderName',\n type: 'input',\n validate(input: string) {\n if (typeof input !== 'string' || input.trim().length === 0) {\n return 'The folder name cannot be empty';\n }\n\n return true;\n },\n },\n ]);\n\n const trimmedName = folderName.trim();\n\n // A new folder whose name matches an existing root folder reuses that folder instead of creating a duplicate.\n const existingRootFolder = findRootFolderByName(groups, trimmedName);\n\n if (existingRootFolder) {\n return { folder: { targetGroupId: existingRootFolder.id } };\n }\n\n return { folder: { newFolderName: trimmedName } };\n};\n\nexport default getFolderPrompts;\n"],"names":["NEW_FOLDER","kind","getFolderPrompts","inquirer","plop","destination","read","readContentStructureFile","getDestBasePath","status","console","warn","sectionKey","sectionKeyForKind","groups","getSectionGroups","file","addToFolder","prompt","message","name","type","default","folderChoices","listFolderChoices","folderChoicesOptions","map","choice","value","id","length","folderChoice","choices","pageSize","folder","targetGroupId","folderName","validate","input","trim","trimmedName","existingRootFolder","findRootFolderByName","newFolderName"],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { readContentStructureFile, getSectionGroups, listFolderChoices, findRootFolderByName, sectionKeyForKind } from '../utils/content-structure.mjs';
|
|
2
|
+
|
|
3
|
+
const NEW_FOLDER = {
|
|
4
|
+
kind: 'new'
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Asks where to place the generated content type in the content-structure folder tree.
|
|
8
|
+
*/ const getFolderPrompts = async (inquirer, plop, { kind, destination })=>{
|
|
9
|
+
// A plugin content type only loads once the plugin is enabled in the app config.
|
|
10
|
+
// Since it can't be determined in this context whether a plugin is enabled or not,
|
|
11
|
+
// a folder assignment could be pruned on the next write to groups.json boot if its
|
|
12
|
+
// referenced content type is found not to exist. I've opted to sidestep this by
|
|
13
|
+
// disabling folder assignment for plugin content-types for the time being.
|
|
14
|
+
if (destination === 'plugin') {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
const read = readContentStructureFile(plop.getDestBasePath());
|
|
18
|
+
if (read.status === 'invalid') {
|
|
19
|
+
console.warn('The content-structure file (src/content-structure/groups.json) could not be read. Skipping folder assignment.');
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
const sectionKey = sectionKeyForKind(kind);
|
|
23
|
+
const groups = read.status === 'ok' ? getSectionGroups(read.file, sectionKey) : [];
|
|
24
|
+
if (groups === null) {
|
|
25
|
+
console.warn(`The "${sectionKey}" section of src/content-structure/groups.json is malformed. Skipping folder assignment.`);
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
const { addToFolder } = await inquirer.prompt([
|
|
29
|
+
{
|
|
30
|
+
message: 'Add this content type to a folder?',
|
|
31
|
+
name: 'addToFolder',
|
|
32
|
+
type: 'confirm',
|
|
33
|
+
default: false
|
|
34
|
+
}
|
|
35
|
+
]);
|
|
36
|
+
if (!addToFolder) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
const folderChoices = listFolderChoices(groups);
|
|
40
|
+
const folderChoicesOptions = folderChoices.map((choice)=>({
|
|
41
|
+
value: {
|
|
42
|
+
kind: 'existing',
|
|
43
|
+
id: choice.value
|
|
44
|
+
},
|
|
45
|
+
name: choice.name
|
|
46
|
+
}));
|
|
47
|
+
if (folderChoicesOptions.length > 0) {
|
|
48
|
+
const { folderChoice } = await inquirer.prompt([
|
|
49
|
+
{
|
|
50
|
+
message: 'Select a folder',
|
|
51
|
+
name: 'folderChoice',
|
|
52
|
+
type: 'list',
|
|
53
|
+
default: 0,
|
|
54
|
+
choices: [
|
|
55
|
+
{
|
|
56
|
+
name: 'Create a new folder',
|
|
57
|
+
value: NEW_FOLDER
|
|
58
|
+
},
|
|
59
|
+
...folderChoicesOptions
|
|
60
|
+
],
|
|
61
|
+
pageSize: folderChoicesOptions.length + 1
|
|
62
|
+
}
|
|
63
|
+
]);
|
|
64
|
+
if (folderChoice.kind === 'existing') {
|
|
65
|
+
return {
|
|
66
|
+
folder: {
|
|
67
|
+
targetGroupId: folderChoice.id
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const { folderName } = await inquirer.prompt([
|
|
73
|
+
{
|
|
74
|
+
message: 'Name of the new folder',
|
|
75
|
+
name: 'folderName',
|
|
76
|
+
type: 'input',
|
|
77
|
+
validate (input) {
|
|
78
|
+
if (typeof input !== 'string' || input.trim().length === 0) {
|
|
79
|
+
return 'The folder name cannot be empty';
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]);
|
|
85
|
+
const trimmedName = folderName.trim();
|
|
86
|
+
// A new folder whose name matches an existing root folder reuses that folder instead of creating a duplicate.
|
|
87
|
+
const existingRootFolder = findRootFolderByName(groups, trimmedName);
|
|
88
|
+
if (existingRootFolder) {
|
|
89
|
+
return {
|
|
90
|
+
folder: {
|
|
91
|
+
targetGroupId: existingRootFolder.id
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
folder: {
|
|
97
|
+
newFolderName: trimmedName
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export { getFolderPrompts as default };
|
|
103
|
+
//# sourceMappingURL=get-folder-prompts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-folder-prompts.mjs","sources":["../../../src/plops/prompts/get-folder-prompts.ts"],"sourcesContent":["import type { NodePlopAPI } from 'plop';\n\nimport {\n readContentStructureFile,\n findRootFolderByName,\n sectionKeyForKind,\n listFolderChoices,\n getSectionGroups,\n type FolderSelection,\n} from '../utils/content-structure';\n\ntype FolderChoiceValue = { kind: 'new' } | { kind: 'existing'; id: string };\n\nconst NEW_FOLDER: FolderChoiceValue = { kind: 'new' };\n\ninterface FolderPromptContext {\n destination?: string;\n kind: string;\n}\n\n/**\n * Asks where to place the generated content type in the content-structure folder tree.\n */\nconst getFolderPrompts = async (\n inquirer: any,\n plop: NodePlopAPI,\n { kind, destination }: FolderPromptContext\n): Promise<{ folder?: FolderSelection }> => {\n // A plugin content type only loads once the plugin is enabled in the app config.\n // Since it can't be determined in this context whether a plugin is enabled or not,\n // a folder assignment could be pruned on the next write to groups.json boot if its\n // referenced content type is found not to exist. I've opted to sidestep this by\n // disabling folder assignment for plugin content-types for the time being.\n\n if (destination === 'plugin') {\n return {};\n }\n\n const read = readContentStructureFile(plop.getDestBasePath());\n\n if (read.status === 'invalid') {\n console.warn(\n 'The content-structure file (src/content-structure/groups.json) could not be read. Skipping folder assignment.'\n );\n\n return {};\n }\n\n const sectionKey = sectionKeyForKind(kind);\n const groups = read.status === 'ok' ? getSectionGroups(read.file, sectionKey) : [];\n\n if (groups === null) {\n console.warn(\n `The \"${sectionKey}\" section of src/content-structure/groups.json is malformed. Skipping folder assignment.`\n );\n\n return {};\n }\n\n const { addToFolder } = await inquirer.prompt([\n {\n message: 'Add this content type to a folder?',\n name: 'addToFolder',\n type: 'confirm',\n default: false,\n },\n ]);\n\n if (!addToFolder) {\n return {};\n }\n\n const folderChoices = listFolderChoices(groups);\n\n const folderChoicesOptions = folderChoices.map((choice) => ({\n value: { kind: 'existing', id: choice.value },\n name: choice.name,\n }));\n\n if (folderChoicesOptions.length > 0) {\n const { folderChoice } = await inquirer.prompt([\n {\n message: 'Select a folder',\n name: 'folderChoice',\n type: 'list',\n default: 0,\n choices: [{ name: 'Create a new folder', value: NEW_FOLDER }, ...folderChoicesOptions],\n pageSize: folderChoicesOptions.length + 1,\n },\n ]);\n\n if (folderChoice.kind === 'existing') {\n return { folder: { targetGroupId: folderChoice.id } };\n }\n }\n\n const { folderName } = await inquirer.prompt([\n {\n message: 'Name of the new folder',\n name: 'folderName',\n type: 'input',\n validate(input: string) {\n if (typeof input !== 'string' || input.trim().length === 0) {\n return 'The folder name cannot be empty';\n }\n\n return true;\n },\n },\n ]);\n\n const trimmedName = folderName.trim();\n\n // A new folder whose name matches an existing root folder reuses that folder instead of creating a duplicate.\n const existingRootFolder = findRootFolderByName(groups, trimmedName);\n\n if (existingRootFolder) {\n return { folder: { targetGroupId: existingRootFolder.id } };\n }\n\n return { folder: { newFolderName: trimmedName } };\n};\n\nexport default getFolderPrompts;\n"],"names":["NEW_FOLDER","kind","getFolderPrompts","inquirer","plop","destination","read","readContentStructureFile","getDestBasePath","status","console","warn","sectionKey","sectionKeyForKind","groups","getSectionGroups","file","addToFolder","prompt","message","name","type","default","folderChoices","listFolderChoices","folderChoicesOptions","map","choice","value","id","length","folderChoice","choices","pageSize","folder","targetGroupId","folderName","validate","input","trim","trimmedName","existingRootFolder","findRootFolderByName","newFolderName"],"mappings":";;AAaA,MAAMA,UAAAA,GAAgC;IAAEC,IAAAA,EAAM;AAAM,CAAA;AAOpD;;IAGA,MAAMC,mBAAmB,OACvBC,QAAAA,EACAC,MACA,EAAEH,IAAI,EAAEI,WAAW,EAAuB,GAAA;;;;;;AAQ1C,IAAA,IAAIA,gBAAgB,QAAA,EAAU;AAC5B,QAAA,OAAO,EAAC;AACV,IAAA;IAEA,MAAMC,IAAAA,GAAOC,wBAAAA,CAAyBH,IAAAA,CAAKI,eAAe,EAAA,CAAA;IAE1D,IAAIF,IAAAA,CAAKG,MAAM,KAAK,SAAA,EAAW;AAC7BC,QAAAA,OAAAA,CAAQC,IAAI,CACV,+GAAA,CAAA;AAGF,QAAA,OAAO,EAAC;AACV,IAAA;AAEA,IAAA,MAAMC,aAAaC,iBAAAA,CAAkBZ,IAAAA,CAAAA;IACrC,MAAMa,MAAAA,GAASR,IAAAA,CAAKG,MAAM,KAAK,IAAA,GAAOM,iBAAiBT,IAAAA,CAAKU,IAAI,EAAEJ,UAAAA,CAAAA,GAAc,EAAE;AAElF,IAAA,IAAIE,WAAW,IAAA,EAAM;AACnBJ,QAAAA,OAAAA,CAAQC,IAAI,CACV,CAAC,KAAK,EAAEC,UAAAA,CAAW,wFAAwF,CAAC,CAAA;AAG9G,QAAA,OAAO,EAAC;AACV,IAAA;AAEA,IAAA,MAAM,EAAEK,WAAW,EAAE,GAAG,MAAMd,QAAAA,CAASe,MAAM,CAAC;AAC5C,QAAA;YACEC,OAAAA,EAAS,oCAAA;YACTC,IAAAA,EAAM,aAAA;YACNC,IAAAA,EAAM,SAAA;YACNC,OAAAA,EAAS;AACX;AACD,KAAA,CAAA;AAED,IAAA,IAAI,CAACL,WAAAA,EAAa;AAChB,QAAA,OAAO,EAAC;AACV,IAAA;AAEA,IAAA,MAAMM,gBAAgBC,iBAAAA,CAAkBV,MAAAA,CAAAA;AAExC,IAAA,MAAMW,uBAAuBF,aAAAA,CAAcG,GAAG,CAAC,CAACC,UAAY;YAC1DC,KAAAA,EAAO;gBAAE3B,IAAAA,EAAM,UAAA;AAAY4B,gBAAAA,EAAAA,EAAIF,OAAOC;AAAM,aAAA;AAC5CR,YAAAA,IAAAA,EAAMO,OAAOP;SACf,CAAA,CAAA;IAEA,IAAIK,oBAAAA,CAAqBK,MAAM,GAAG,CAAA,EAAG;AACnC,QAAA,MAAM,EAAEC,YAAY,EAAE,GAAG,MAAM5B,QAAAA,CAASe,MAAM,CAAC;AAC7C,YAAA;gBACEC,OAAAA,EAAS,iBAAA;gBACTC,IAAAA,EAAM,cAAA;gBACNC,IAAAA,EAAM,MAAA;gBACNC,OAAAA,EAAS,CAAA;gBACTU,OAAAA,EAAS;AAAC,oBAAA;wBAAEZ,IAAAA,EAAM,qBAAA;wBAAuBQ,KAAAA,EAAO5B;AAAW,qBAAA;AAAMyB,oBAAAA,GAAAA;AAAqB,iBAAA;gBACtFQ,QAAAA,EAAUR,oBAAAA,CAAqBK,MAAM,GAAG;AAC1C;AACD,SAAA,CAAA;QAED,IAAIC,YAAAA,CAAa9B,IAAI,KAAK,UAAA,EAAY;YACpC,OAAO;gBAAEiC,MAAAA,EAAQ;AAAEC,oBAAAA,aAAAA,EAAeJ,aAAaF;AAAG;AAAE,aAAA;AACtD,QAAA;AACF,IAAA;AAEA,IAAA,MAAM,EAAEO,UAAU,EAAE,GAAG,MAAMjC,QAAAA,CAASe,MAAM,CAAC;AAC3C,QAAA;YACEC,OAAAA,EAAS,wBAAA;YACTC,IAAAA,EAAM,YAAA;YACNC,IAAAA,EAAM,OAAA;AACNgB,YAAAA,QAAAA,CAAAA,CAASC,KAAa,EAAA;gBACpB,IAAI,OAAOA,UAAU,QAAA,IAAYA,KAAAA,CAAMC,IAAI,EAAA,CAAGT,MAAM,KAAK,CAAA,EAAG;oBAC1D,OAAO,iCAAA;AACT,gBAAA;gBAEA,OAAO,IAAA;AACT,YAAA;AACF;AACD,KAAA,CAAA;IAED,MAAMU,WAAAA,GAAcJ,WAAWG,IAAI,EAAA;;IAGnC,MAAME,kBAAAA,GAAqBC,qBAAqB5B,MAAAA,EAAQ0B,WAAAA,CAAAA;AAExD,IAAA,IAAIC,kBAAAA,EAAoB;QACtB,OAAO;YAAEP,MAAAA,EAAQ;AAAEC,gBAAAA,aAAAA,EAAeM,mBAAmBZ;AAAG;AAAE,SAAA;AAC5D,IAAA;IAEA,OAAO;QAAEK,MAAAA,EAAQ;YAAES,aAAAA,EAAeH;AAAY;AAAE,KAAA;AAClD;;;;"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type FolderSelection = {
|
|
2
|
+
targetGroupId: string;
|
|
3
|
+
} | {
|
|
4
|
+
newFolderName: string;
|
|
5
|
+
};
|
|
6
|
+
export interface FolderChoice {
|
|
7
|
+
value: string;
|
|
8
|
+
name: string;
|
|
9
|
+
}
|
|
10
|
+
export type ContentStructureReadResult = {
|
|
11
|
+
status: 'ok';
|
|
12
|
+
file: any;
|
|
13
|
+
} | {
|
|
14
|
+
status: 'invalid';
|
|
15
|
+
} | {
|
|
16
|
+
status: 'absent';
|
|
17
|
+
};
|
|
18
|
+
export declare const getContentStructureFilePath: (destBasePath: string) => string;
|
|
19
|
+
export declare const readContentStructureFile: (destBasePath: string) => ContentStructureReadResult;
|
|
20
|
+
export declare const sectionKeyForKind: (kind: string) => "collectionTypes" | "singleTypes";
|
|
21
|
+
export declare const getSectionGroups: (file: any, sectionKey: string) => any[] | null;
|
|
22
|
+
export declare const listFolderChoices: (groups: any[]) => FolderChoice[];
|
|
23
|
+
/**
|
|
24
|
+
* Groups whose parent id does not exist are considered root level entries.
|
|
25
|
+
*/
|
|
26
|
+
export declare const findRootFolderByName: (groups: any[], name: string) => any;
|
|
27
|
+
export declare const generateFolderId: () => string;
|
|
28
|
+
export interface AssignContentTypeToFolderOptions {
|
|
29
|
+
folder: FolderSelection;
|
|
30
|
+
destBasePath: string;
|
|
31
|
+
kind: string;
|
|
32
|
+
uid: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validates the folder selection and stages the assignment of a content type in
|
|
36
|
+
* `<destBasePath>/content-structure/groups.json`, creating the file (or a new root
|
|
37
|
+
* folder) in memory when needed.
|
|
38
|
+
*
|
|
39
|
+
* @return A commit function that writes the staged result to disk.
|
|
40
|
+
*/
|
|
41
|
+
export declare const planContentTypeToFolder: (options: AssignContentTypeToFolderOptions) => (() => void);
|
|
42
|
+
//# sourceMappingURL=content-structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-structure.d.ts","sourceRoot":"","sources":["../../../src/plops/utils/content-structure.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GAAG;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,GAC3B;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEzB,eAAO,MAAM,2BAA2B,GAAI,cAAc,MAAM,KAAG,MAElE,CAAC;AAMF,eAAO,MAAM,wBAAwB,GAAI,cAAc,MAAM,KAAG,0BAoB/D,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,iBAAiB,GAAG,aAEpE,CAAC;AAsBF,eAAO,MAAM,gBAAgB,GAAI,MAAM,GAAG,EAAE,YAAY,MAAM,KAAG,GAAG,EAAE,GAAG,IAYxE,CAAC;AAcF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,GAAG,EAAE,KAAG,YAAY,EA4D7D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,GAAG,EAAE,EAAE,MAAM,MAAM,KAAG,GAoBlE,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAO,MAGnC,CAAC;AAiFF,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,eAAe,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,gCAAgC,KACxC,CAAC,MAAM,IAAI,CA+Bb,CAAC"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var node_path = require('node:path');
|
|
4
|
+
var node_crypto = require('node:crypto');
|
|
5
|
+
var fs = require('fs-extra');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
10
|
+
|
|
11
|
+
const getContentStructureFilePath = (destBasePath)=>{
|
|
12
|
+
return node_path.join(destBasePath, 'content-structure', 'groups.json');
|
|
13
|
+
};
|
|
14
|
+
const isRecord = (value)=>{
|
|
15
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
16
|
+
};
|
|
17
|
+
const readContentStructureFile = (destBasePath)=>{
|
|
18
|
+
const filePath = getContentStructureFilePath(destBasePath);
|
|
19
|
+
if (!fs__default.default.pathExistsSync(filePath)) {
|
|
20
|
+
return {
|
|
21
|
+
status: 'absent'
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = fs__default.default.readJSONSync(filePath);
|
|
27
|
+
} catch {
|
|
28
|
+
return {
|
|
29
|
+
status: 'invalid'
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (!isRecord(parsed) || parsed.version !== 1 || !isRecord(parsed.sections)) {
|
|
33
|
+
return {
|
|
34
|
+
status: 'invalid'
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
status: 'ok',
|
|
39
|
+
file: parsed
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
const sectionKeyForKind = (kind)=>{
|
|
43
|
+
return kind === 'singleType' ? 'singleTypes' : 'collectionTypes';
|
|
44
|
+
};
|
|
45
|
+
const isWellFormedGroup = (group)=>{
|
|
46
|
+
if (!isRecord(group)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (typeof group.id !== 'string' || group.id.length === 0) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (typeof group.name !== 'string' || group.name.trim().length === 0) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (group.parent !== null && typeof group.parent !== 'string') {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return Array.isArray(group.children);
|
|
59
|
+
};
|
|
60
|
+
const getSectionGroups = (file, sectionKey)=>{
|
|
61
|
+
const section = file.sections[sectionKey];
|
|
62
|
+
if (section === undefined || section === null) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
if (!isRecord(section) || !Array.isArray(section.groups)) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return section.groups;
|
|
69
|
+
};
|
|
70
|
+
const isGroupChild = (child)=>{
|
|
71
|
+
return isRecord(child) && child.type === 'group' && typeof child.id === 'string';
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* A group is an effective root when it has no parent, or its parent id points
|
|
75
|
+
* at a group that no longer exists.
|
|
76
|
+
*/ const isEffectivelyRoot = (group, knownIds)=>{
|
|
77
|
+
return group.parent === null || !knownIds.has(group.parent);
|
|
78
|
+
};
|
|
79
|
+
const listFolderChoices = (groups)=>{
|
|
80
|
+
const wellFormed = groups.filter(isWellFormedGroup);
|
|
81
|
+
const choices = [];
|
|
82
|
+
const seen = new Set();
|
|
83
|
+
const byId = new Map();
|
|
84
|
+
for (const group of wellFormed){
|
|
85
|
+
if (byId.has(group.id)) continue;
|
|
86
|
+
byId.set(group.id, group);
|
|
87
|
+
}
|
|
88
|
+
const knownIds = new Set(byId.keys());
|
|
89
|
+
const childGroupsOf = (parent)=>{
|
|
90
|
+
const listed = parent.children.filter(isGroupChild).map((child)=>{
|
|
91
|
+
return byId.get(child.id);
|
|
92
|
+
}).filter((group)=>{
|
|
93
|
+
return group !== undefined && group.parent === parent.id;
|
|
94
|
+
});
|
|
95
|
+
const unlisted = wellFormed.filter((group)=>{
|
|
96
|
+
return group.parent === parent.id && !listed.includes(group);
|
|
97
|
+
});
|
|
98
|
+
return [
|
|
99
|
+
...listed,
|
|
100
|
+
...unlisted
|
|
101
|
+
];
|
|
102
|
+
};
|
|
103
|
+
const visit = (parent, path)=>{
|
|
104
|
+
const children = (()=>{
|
|
105
|
+
if (parent !== null) {
|
|
106
|
+
return childGroupsOf(parent);
|
|
107
|
+
}
|
|
108
|
+
return wellFormed.filter((group)=>{
|
|
109
|
+
return isEffectivelyRoot(group, knownIds);
|
|
110
|
+
});
|
|
111
|
+
})();
|
|
112
|
+
for (const group of children){
|
|
113
|
+
if (seen.has(group.id)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
seen.add(group.id);
|
|
117
|
+
const groupPath = [
|
|
118
|
+
...path,
|
|
119
|
+
group.name
|
|
120
|
+
];
|
|
121
|
+
choices.push({
|
|
122
|
+
name: groupPath.join(' / '),
|
|
123
|
+
value: group.id
|
|
124
|
+
});
|
|
125
|
+
visit(group, groupPath);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
visit(null, []);
|
|
129
|
+
return choices;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Groups whose parent id does not exist are considered root level entries.
|
|
133
|
+
*/ const findRootFolderByName = (groups, name)=>{
|
|
134
|
+
const target = name.trim().toLowerCase();
|
|
135
|
+
if (target.length === 0) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const knownIds = new Set(groups.filter(isWellFormedGroup).map(({ id })=>id));
|
|
139
|
+
return groups.find((group)=>{
|
|
140
|
+
if (!isWellFormedGroup(group)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (!isEffectivelyRoot(group, knownIds)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
return group.name.trim().toLowerCase() === target;
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const generateFolderId = ()=>{
|
|
150
|
+
const body = node_crypto.randomUUID().replace(/-/g, '').slice(0, 24);
|
|
151
|
+
return `grp_${body}`;
|
|
152
|
+
};
|
|
153
|
+
const createEmptyContentStructureFile = ()=>{
|
|
154
|
+
return {
|
|
155
|
+
version: 1,
|
|
156
|
+
sections: {
|
|
157
|
+
collectionTypes: {
|
|
158
|
+
groups: []
|
|
159
|
+
},
|
|
160
|
+
singleTypes: {
|
|
161
|
+
groups: []
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
const ensureSectionGroups = (file, sectionKey)=>{
|
|
167
|
+
const section = file.sections[sectionKey];
|
|
168
|
+
if (section === undefined || section === null) {
|
|
169
|
+
file.sections[sectionKey] = {
|
|
170
|
+
groups: []
|
|
171
|
+
};
|
|
172
|
+
return file.sections[sectionKey].groups;
|
|
173
|
+
}
|
|
174
|
+
if (!isRecord(section) || !Array.isArray(section.groups)) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
return section.groups;
|
|
178
|
+
};
|
|
179
|
+
const resolveTargetGroup = (groups, folder)=>{
|
|
180
|
+
if ('targetGroupId' in folder) {
|
|
181
|
+
const target = groups.find((group)=>{
|
|
182
|
+
return isWellFormedGroup(group) && group.id === folder.targetGroupId;
|
|
183
|
+
});
|
|
184
|
+
if (!target) {
|
|
185
|
+
throw new Error(`No usable folder with id "${folder.targetGroupId}" exists in groups.json.`);
|
|
186
|
+
}
|
|
187
|
+
return target;
|
|
188
|
+
}
|
|
189
|
+
const hasUsableName = typeof folder.newFolderName === 'string';
|
|
190
|
+
const name = hasUsableName ? folder.newFolderName.trim() : '';
|
|
191
|
+
if (name.length === 0) {
|
|
192
|
+
throw new Error('The new folder name must be a non-empty string.');
|
|
193
|
+
}
|
|
194
|
+
const existingRootFolder = findRootFolderByName(groups, name);
|
|
195
|
+
if (existingRootFolder) {
|
|
196
|
+
return existingRootFolder;
|
|
197
|
+
}
|
|
198
|
+
const created = {
|
|
199
|
+
id: generateFolderId(),
|
|
200
|
+
children: [],
|
|
201
|
+
parent: null,
|
|
202
|
+
name
|
|
203
|
+
};
|
|
204
|
+
groups.push(created);
|
|
205
|
+
return created;
|
|
206
|
+
};
|
|
207
|
+
const removeContentTypeFromSection = (groups, uid)=>{
|
|
208
|
+
for (const group of groups){
|
|
209
|
+
if (!isWellFormedGroup(group)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
group.children = group.children.filter((child)=>{
|
|
213
|
+
if (!isRecord(child)) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
return !(child.type === 'contentType' && child.uid === uid);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Validates the folder selection and stages the assignment of a content type in
|
|
222
|
+
* `<destBasePath>/content-structure/groups.json`, creating the file (or a new root
|
|
223
|
+
* folder) in memory when needed.
|
|
224
|
+
*
|
|
225
|
+
* @return A commit function that writes the staged result to disk.
|
|
226
|
+
*/ const planContentTypeToFolder = (options)=>{
|
|
227
|
+
const { destBasePath, kind, uid, folder } = options;
|
|
228
|
+
const filePath = getContentStructureFilePath(destBasePath);
|
|
229
|
+
const read = readContentStructureFile(destBasePath);
|
|
230
|
+
if (read.status === 'invalid') {
|
|
231
|
+
throw new Error(`Cannot assign "${uid}" to a folder: ${filePath} exists but is not a valid content-structure file.`);
|
|
232
|
+
}
|
|
233
|
+
const file = read.status === 'ok' ? read.file : createEmptyContentStructureFile();
|
|
234
|
+
const sectionKey = sectionKeyForKind(kind);
|
|
235
|
+
const groups = ensureSectionGroups(file, sectionKey);
|
|
236
|
+
if (groups === null) {
|
|
237
|
+
throw new Error(`Cannot assign "${uid}" to a folder: the "${sectionKey}" section of ${filePath} is malformed.`);
|
|
238
|
+
}
|
|
239
|
+
const targetGroup = resolveTargetGroup(groups, folder);
|
|
240
|
+
removeContentTypeFromSection(groups, uid);
|
|
241
|
+
targetGroup.children.push({
|
|
242
|
+
type: 'contentType',
|
|
243
|
+
uid
|
|
244
|
+
});
|
|
245
|
+
return ()=>{
|
|
246
|
+
fs__default.default.outputJSONSync(filePath, file, {
|
|
247
|
+
spaces: 2
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
exports.findRootFolderByName = findRootFolderByName;
|
|
253
|
+
exports.generateFolderId = generateFolderId;
|
|
254
|
+
exports.getContentStructureFilePath = getContentStructureFilePath;
|
|
255
|
+
exports.getSectionGroups = getSectionGroups;
|
|
256
|
+
exports.listFolderChoices = listFolderChoices;
|
|
257
|
+
exports.planContentTypeToFolder = planContentTypeToFolder;
|
|
258
|
+
exports.readContentStructureFile = readContentStructureFile;
|
|
259
|
+
exports.sectionKeyForKind = sectionKeyForKind;
|
|
260
|
+
//# sourceMappingURL=content-structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-structure.js","sources":["../../../src/plops/utils/content-structure.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport fs from 'fs-extra';\n\nexport type FolderSelection = { targetGroupId: string } | { newFolderName: string };\n\nexport interface FolderChoice {\n value: string;\n name: string;\n}\n\nexport type ContentStructureReadResult =\n | { status: 'ok'; file: any }\n | { status: 'invalid' }\n | { status: 'absent' };\n\nexport const getContentStructureFilePath = (destBasePath: string): string => {\n return join(destBasePath, 'content-structure', 'groups.json');\n};\n\nconst isRecord = (value: any): boolean => {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n};\n\nexport const readContentStructureFile = (destBasePath: string): ContentStructureReadResult => {\n const filePath = getContentStructureFilePath(destBasePath);\n\n if (!fs.pathExistsSync(filePath)) {\n return { status: 'absent' };\n }\n\n let parsed: any;\n\n try {\n parsed = fs.readJSONSync(filePath);\n } catch {\n return { status: 'invalid' };\n }\n\n if (!isRecord(parsed) || parsed.version !== 1 || !isRecord(parsed.sections)) {\n return { status: 'invalid' };\n }\n\n return { status: 'ok', file: parsed };\n};\n\nexport const sectionKeyForKind = (kind: string): 'collectionTypes' | 'singleTypes' => {\n return kind === 'singleType' ? 'singleTypes' : 'collectionTypes';\n};\n\nconst isWellFormedGroup = (group: any): boolean => {\n if (!isRecord(group)) {\n return false;\n }\n\n if (typeof group.id !== 'string' || group.id.length === 0) {\n return false;\n }\n\n if (typeof group.name !== 'string' || group.name.trim().length === 0) {\n return false;\n }\n\n if (group.parent !== null && typeof group.parent !== 'string') {\n return false;\n }\n\n return Array.isArray(group.children);\n};\n\nexport const getSectionGroups = (file: any, sectionKey: string): any[] | null => {\n const section = file.sections[sectionKey];\n\n if (section === undefined || section === null) {\n return [];\n }\n\n if (!isRecord(section) || !Array.isArray(section.groups)) {\n return null;\n }\n\n return section.groups;\n};\n\nconst isGroupChild = (child: any): boolean => {\n return isRecord(child) && child.type === 'group' && typeof child.id === 'string';\n};\n\n/**\n * A group is an effective root when it has no parent, or its parent id points\n * at a group that no longer exists.\n */\nconst isEffectivelyRoot = (group: any, knownIds: Set<string>): boolean => {\n return group.parent === null || !knownIds.has(group.parent);\n};\n\nexport const listFolderChoices = (groups: any[]): FolderChoice[] => {\n const wellFormed = groups.filter(isWellFormedGroup);\n\n const choices: FolderChoice[] = [];\n const seen = new Set<string>();\n\n const byId = new Map<string, any>();\n\n for (const group of wellFormed) {\n if (byId.has(group.id)) continue;\n byId.set(group.id, group);\n }\n\n const knownIds = new Set(byId.keys());\n\n const childGroupsOf = (parent: any): any[] => {\n const listed = parent.children\n .filter(isGroupChild)\n .map((child: any) => {\n return byId.get(child.id);\n })\n .filter((group: any) => {\n return group !== undefined && group.parent === parent.id;\n });\n\n const unlisted = wellFormed.filter((group) => {\n return group.parent === parent.id && !listed.includes(group);\n });\n\n return [...listed, ...unlisted];\n };\n\n const visit = (parent: any, path: string[]) => {\n const children = (() => {\n if (parent !== null) {\n return childGroupsOf(parent);\n }\n\n return wellFormed.filter((group) => {\n return isEffectivelyRoot(group, knownIds);\n });\n })();\n\n for (const group of children) {\n if (seen.has(group.id)) {\n continue;\n }\n\n seen.add(group.id);\n\n const groupPath = [...path, group.name];\n choices.push({ name: groupPath.join(' / '), value: group.id });\n\n visit(group, groupPath);\n }\n };\n\n visit(null, []);\n\n return choices;\n};\n\n/**\n * Groups whose parent id does not exist are considered root level entries.\n */\nexport const findRootFolderByName = (groups: any[], name: string): any => {\n const target = name.trim().toLowerCase();\n\n if (target.length === 0) {\n return undefined;\n }\n\n const knownIds = new Set(groups.filter(isWellFormedGroup).map(({ id }) => id));\n\n return groups.find((group) => {\n if (!isWellFormedGroup(group)) {\n return false;\n }\n\n if (!isEffectivelyRoot(group, knownIds)) {\n return false;\n }\n\n return group.name.trim().toLowerCase() === target;\n });\n};\n\nexport const generateFolderId = (): string => {\n const body = randomUUID().replace(/-/g, '').slice(0, 24);\n return `grp_${body}`;\n};\n\nconst createEmptyContentStructureFile = () => {\n return {\n version: 1,\n sections: {\n collectionTypes: { groups: [] },\n singleTypes: { groups: [] },\n },\n };\n};\n\nconst ensureSectionGroups = (file: any, sectionKey: string): any[] | null => {\n const section = file.sections[sectionKey];\n\n if (section === undefined || section === null) {\n file.sections[sectionKey] = { groups: [] };\n return file.sections[sectionKey].groups;\n }\n\n if (!isRecord(section) || !Array.isArray(section.groups)) {\n return null;\n }\n\n return section.groups;\n};\n\nconst resolveTargetGroup = (groups: any[], folder: FolderSelection): any => {\n if ('targetGroupId' in folder) {\n const target = groups.find((group) => {\n return isWellFormedGroup(group) && group.id === folder.targetGroupId;\n });\n\n if (!target) {\n throw new Error(`No usable folder with id \"${folder.targetGroupId}\" exists in groups.json.`);\n }\n\n return target;\n }\n\n const hasUsableName = typeof folder.newFolderName === 'string';\n const name = hasUsableName ? folder.newFolderName.trim() : '';\n\n if (name.length === 0) {\n throw new Error('The new folder name must be a non-empty string.');\n }\n\n const existingRootFolder = findRootFolderByName(groups, name);\n\n if (existingRootFolder) {\n return existingRootFolder;\n }\n\n const created = {\n id: generateFolderId(),\n children: [],\n parent: null,\n name,\n };\n\n groups.push(created);\n\n return created;\n};\n\nconst removeContentTypeFromSection = (groups: any[], uid: string): void => {\n for (const group of groups) {\n if (!isWellFormedGroup(group)) {\n continue;\n }\n\n group.children = group.children.filter((child: any) => {\n if (!isRecord(child)) {\n return true;\n }\n\n return !(child.type === 'contentType' && child.uid === uid);\n });\n }\n};\n\nexport interface AssignContentTypeToFolderOptions {\n folder: FolderSelection;\n destBasePath: string;\n kind: string;\n uid: string;\n}\n\n/**\n * Validates the folder selection and stages the assignment of a content type in\n * `<destBasePath>/content-structure/groups.json`, creating the file (or a new root\n * folder) in memory when needed.\n *\n * @return A commit function that writes the staged result to disk.\n */\nexport const planContentTypeToFolder = (\n options: AssignContentTypeToFolderOptions\n): (() => void) => {\n const { destBasePath, kind, uid, folder } = options;\n const filePath = getContentStructureFilePath(destBasePath);\n\n const read = readContentStructureFile(destBasePath);\n\n if (read.status === 'invalid') {\n throw new Error(\n `Cannot assign \"${uid}\" to a folder: ${filePath} exists but is not a valid content-structure file.`\n );\n }\n\n const file = read.status === 'ok' ? read.file : createEmptyContentStructureFile();\n\n const sectionKey = sectionKeyForKind(kind);\n const groups = ensureSectionGroups(file, sectionKey);\n\n if (groups === null) {\n throw new Error(\n `Cannot assign \"${uid}\" to a folder: the \"${sectionKey}\" section of ${filePath} is malformed.`\n );\n }\n\n const targetGroup = resolveTargetGroup(groups, folder);\n\n removeContentTypeFromSection(groups, uid);\n targetGroup.children.push({ type: 'contentType', uid });\n\n return () => {\n fs.outputJSONSync(filePath, file, { spaces: 2 });\n };\n};\n"],"names":["getContentStructureFilePath","destBasePath","join","isRecord","value","Array","isArray","readContentStructureFile","filePath","fs","pathExistsSync","status","parsed","readJSONSync","version","sections","file","sectionKeyForKind","kind","isWellFormedGroup","group","id","length","name","trim","parent","children","getSectionGroups","sectionKey","section","undefined","groups","isGroupChild","child","type","isEffectivelyRoot","knownIds","has","listFolderChoices","wellFormed","filter","choices","seen","Set","byId","Map","set","keys","childGroupsOf","listed","map","get","unlisted","includes","visit","path","add","groupPath","push","findRootFolderByName","target","toLowerCase","find","generateFolderId","body","randomUUID","replace","slice","createEmptyContentStructureFile","collectionTypes","singleTypes","ensureSectionGroups","resolveTargetGroup","folder","targetGroupId","Error","hasUsableName","newFolderName","existingRootFolder","created","removeContentTypeFromSection","uid","planContentTypeToFolder","options","read","targetGroup","outputJSONSync","spaces"],"mappings":"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"}
|