@stackbit/cms-core 0.1.25-cloudinary-presets.0 → 0.1.26-gitcms.0
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/content-store-types.d.ts +0 -2
- package/dist/content-store-types.d.ts.map +1 -1
- package/dist/content-store.d.ts +5 -0
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +11 -6
- package/dist/content-store.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/services/git.d.ts +35 -0
- package/dist/services/git.d.ts.map +1 -0
- package/dist/services/git.js +182 -0
- package/dist/services/git.js.map +1 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +15 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/run.d.ts +17 -0
- package/dist/services/run.d.ts.map +1 -0
- package/dist/services/run.js +54 -0
- package/dist/services/run.js.map +1 -0
- package/dist/utils/create-update-csi-docs.d.ts.map +1 -1
- package/dist/utils/create-update-csi-docs.js +18 -5
- package/dist/utils/create-update-csi-docs.js.map +1 -1
- package/dist/utils/duplicate-document.js +0 -7
- package/dist/utils/duplicate-document.js.map +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/store-to-api-docs-converter.d.ts +3 -3
- package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
- package/dist/utils/store-to-api-docs-converter.js +66 -45
- package/dist/utils/store-to-api-docs-converter.js.map +1 -1
- package/dist/utils/store-to-csi-docs-converter.js +0 -4
- package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
- package/package.json +9 -6
- package/src/content-store-types.ts +1 -5
- package/src/content-store.ts +17 -7
- package/src/index.ts +1 -0
- package/src/services/git.ts +227 -0
- package/src/services/index.ts +2 -0
- package/src/services/run.ts +59 -0
- package/src/utils/create-update-csi-docs.ts +18 -5
- package/src/utils/duplicate-document.ts +1 -8
- package/src/utils/store-to-api-docs-converter.ts +63 -44
- package/src/utils/store-to-csi-docs-converter.ts +0 -4
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { v4 as uuid } from 'uuid';
|
|
5
|
+
import fse from 'fs-extra';
|
|
6
|
+
|
|
7
|
+
import { GitServiceInterface, GitFileObject, GitAuthor, GitCommitLogEntry, Logger } from '@stackbit/types';
|
|
8
|
+
import { Worker } from '@stackbit/utils';
|
|
9
|
+
|
|
10
|
+
import { RunService } from './run';
|
|
11
|
+
import { DocumentStatus } from '@stackbit/types';
|
|
12
|
+
|
|
13
|
+
const GIT_LOG_CHANGE_TYPES: Record<string, DocumentStatus> = {
|
|
14
|
+
M: 'modified',
|
|
15
|
+
A: 'added',
|
|
16
|
+
D: 'deleted'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export class GitService implements GitServiceInterface {
|
|
20
|
+
private readonly repoUrl: string;
|
|
21
|
+
private readonly repoDir: string;
|
|
22
|
+
private readonly repoBranch: string;
|
|
23
|
+
private readonly repoPublishBranch: string;
|
|
24
|
+
private readonly worker: Worker;
|
|
25
|
+
private readonly run: RunService;
|
|
26
|
+
private readonly logger: Logger;
|
|
27
|
+
private readonly userLogger: Logger;
|
|
28
|
+
|
|
29
|
+
private branchFetched: boolean = false;
|
|
30
|
+
|
|
31
|
+
constructor(options: {
|
|
32
|
+
repoUrl: string;
|
|
33
|
+
repoDir: string;
|
|
34
|
+
repoBranch: string;
|
|
35
|
+
repoPublishBranch: string;
|
|
36
|
+
worker: Worker;
|
|
37
|
+
runService: RunService;
|
|
38
|
+
logger: Logger;
|
|
39
|
+
userLogger: Logger;
|
|
40
|
+
}) {
|
|
41
|
+
this.repoUrl = options.repoUrl;
|
|
42
|
+
this.repoDir = options.repoDir;
|
|
43
|
+
this.repoBranch = options.repoBranch;
|
|
44
|
+
this.repoPublishBranch = options.repoPublishBranch;
|
|
45
|
+
this.worker = options.worker;
|
|
46
|
+
this.run = options.runService;
|
|
47
|
+
this.logger = options.logger;
|
|
48
|
+
this.userLogger = options.userLogger;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private async commit(author: GitAuthor, files: GitFileObject[]) {
|
|
52
|
+
const filePaths = _.map(files, 'filePath');
|
|
53
|
+
this.logger.debug('[git] Commit scheduled', filePaths);
|
|
54
|
+
return this.worker.schedule(async () => {
|
|
55
|
+
this.logger.debug('[git] Commit running', filePaths);
|
|
56
|
+
const message = files
|
|
57
|
+
.reduce((messages: string[], file) => {
|
|
58
|
+
messages.push(`${path.parse(file.filePath).base}: ${file.description}`);
|
|
59
|
+
return messages;
|
|
60
|
+
}, [])
|
|
61
|
+
.join('.\n');
|
|
62
|
+
await this.run.command('git', ['add', ...filePaths], this.repoDir);
|
|
63
|
+
await this.run.command(
|
|
64
|
+
'git',
|
|
65
|
+
['commit', '--no-verify', '--author', `${author.name || author.email} <${author.email}>`, '-m', message],
|
|
66
|
+
this.repoDir
|
|
67
|
+
);
|
|
68
|
+
this.logger.debug('[git] Commit done', filePaths);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private push() {
|
|
73
|
+
this.logger.debug('[git] Push scheduled');
|
|
74
|
+
return this.worker.schedule(async () => {
|
|
75
|
+
this.logger.debug('[git] Push running');
|
|
76
|
+
await this.run.command('git', ['pull', 'origin', '--rebase', '--autostash', '-Xtheirs'], this.repoDir);
|
|
77
|
+
await this.run.command('git', ['push', 'origin'], this.repoDir);
|
|
78
|
+
this.logger.debug('[git] Push done');
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async commitAndPush(author: GitAuthor, files: GitFileObject[]) {
|
|
83
|
+
await this.commit(author, files);
|
|
84
|
+
return this.push();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pull() {
|
|
88
|
+
this.logger.debug('[git] Pull scheduled');
|
|
89
|
+
return this.worker.schedule(async () => {
|
|
90
|
+
this.logger.debug('[git] Pull running');
|
|
91
|
+
await this.run.command('git', ['pull', 'origin', '--rebase', '--autostash', '-Xtheirs'], this.repoDir);
|
|
92
|
+
this.logger.debug('[git] Pull done');
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private async publishAll(author: GitAuthor) {
|
|
97
|
+
this.logger.debug('[git] Publish all started');
|
|
98
|
+
const publishDir = path.join(os.tmpdir(), uuid());
|
|
99
|
+
await this.run.command('git', ['clone', this.repoUrl, '--branch', this.repoPublishBranch, publishDir]);
|
|
100
|
+
try {
|
|
101
|
+
await this.run.command('git', ['merge', `origin/${this.repoBranch}`, this.repoPublishBranch, '-Xtheirs'], publishDir);
|
|
102
|
+
await this.run
|
|
103
|
+
.command('git', ['commit', '--author', `${author.name || author.email} <${author.email}>`, `-m`, 'Publish'], publishDir)
|
|
104
|
+
.catch(() => {});
|
|
105
|
+
await this.run.command('git', ['push', 'origin'], publishDir);
|
|
106
|
+
} finally {
|
|
107
|
+
await fse.remove(publishDir);
|
|
108
|
+
}
|
|
109
|
+
this.logger.debug('[git] Publish all done');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private async publishFiles(author: GitAuthor, filePaths: string[]) {
|
|
113
|
+
this.logger.debug('[git] Publish files started', filePaths);
|
|
114
|
+
const publishDir = path.join(os.tmpdir(), uuid());
|
|
115
|
+
await this.run.command('git', ['clone', this.repoUrl, '--branch', this.repoPublishBranch, publishDir]);
|
|
116
|
+
try {
|
|
117
|
+
await Promise.all(
|
|
118
|
+
filePaths.map(async (filePath) => {
|
|
119
|
+
const destFilePath = path.join(publishDir, filePath);
|
|
120
|
+
await fse.ensureDir(path.dirname(destFilePath));
|
|
121
|
+
return fse.copy(path.join(this.repoDir, filePath), destFilePath);
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
await this.run.command('git', ['checkout', '-b', 'stackbit-publish-branch'], publishDir);
|
|
126
|
+
await this.run.command('git', ['add', ...filePaths], publishDir);
|
|
127
|
+
await this.run.command('git', ['commit', '--author', `${author.name || author.email} <${author.email}>`, `-m`, 'Publish'], publishDir);
|
|
128
|
+
|
|
129
|
+
await this.run.command('git', ['checkout', this.repoBranch], publishDir);
|
|
130
|
+
await this.run.command('git', ['merge', 'stackbit-publish-branch', this.repoBranch, '-Xtheirs'], publishDir);
|
|
131
|
+
|
|
132
|
+
await this.run.command('git', ['checkout', this.repoPublishBranch], publishDir);
|
|
133
|
+
await this.run.command('git', ['merge', 'stackbit-publish-branch', this.repoPublishBranch, '-Xtheirs'], publishDir);
|
|
134
|
+
|
|
135
|
+
await this.run.command('git', ['push', 'origin', this.repoPublishBranch, this.repoBranch], publishDir);
|
|
136
|
+
} finally {
|
|
137
|
+
await fse.remove(publishDir);
|
|
138
|
+
}
|
|
139
|
+
this.logger.debug('[git] Publish files done', filePaths);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
publish(author: GitAuthor, filePaths?: string[]): Promise<void> {
|
|
143
|
+
this.logger.debug('[git] Publish scheduled');
|
|
144
|
+
return this.worker.schedule(async () => {
|
|
145
|
+
if (filePaths) {
|
|
146
|
+
if (!filePaths.length) {
|
|
147
|
+
this.logger.debug('[git] Nothing to publish');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
return this.publishFiles(author, filePaths);
|
|
151
|
+
} else {
|
|
152
|
+
return this.publishAll(author);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private parseGitCommitAuthor(author?: string) {
|
|
158
|
+
if (!author) {
|
|
159
|
+
return author;
|
|
160
|
+
}
|
|
161
|
+
const regex = /(.*)\((.*)\)/;
|
|
162
|
+
const match = author.match(regex);
|
|
163
|
+
if (match) {
|
|
164
|
+
const [authorEmail, authorName] = match.slice(1);
|
|
165
|
+
|
|
166
|
+
if (authorName === 'Stackbit Code Editor') {
|
|
167
|
+
return 'stackbit';
|
|
168
|
+
}
|
|
169
|
+
return authorEmail ? authorEmail.toLowerCase() : author;
|
|
170
|
+
}
|
|
171
|
+
return author;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async diff(): Promise<string[]> {
|
|
175
|
+
this.logger.debug('[git] Diff check scheduled');
|
|
176
|
+
return this.worker.schedule(async () => {
|
|
177
|
+
this.logger.debug('[git] Diff check running');
|
|
178
|
+
const result = await this.run.command(
|
|
179
|
+
'git',
|
|
180
|
+
[
|
|
181
|
+
'diff',
|
|
182
|
+
'--name-only',
|
|
183
|
+
'--no-renames', // this flag makes sure we get both old and new name of renamed file
|
|
184
|
+
`origin/${this.repoPublishBranch}..${this.repoBranch}`
|
|
185
|
+
],
|
|
186
|
+
this.repoDir
|
|
187
|
+
);
|
|
188
|
+
this.logger.debug('[git] Diff check done');
|
|
189
|
+
return result.stdout.split('\n').filter(Boolean);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async commitLog(): Promise<GitCommitLogEntry[]> {
|
|
194
|
+
this.logger.debug('[git] Changes check scheduled');
|
|
195
|
+
return this.worker.schedule(async () => {
|
|
196
|
+
this.logger.debug('[git] Changes check running');
|
|
197
|
+
if (!this.branchFetched) {
|
|
198
|
+
await this.run.command('git', ['fetch', 'origin', `${this.repoPublishBranch}:${this.repoPublishBranch}`], this.repoDir);
|
|
199
|
+
this.branchFetched = true;
|
|
200
|
+
}
|
|
201
|
+
const logResult = await this.run.command(
|
|
202
|
+
'git',
|
|
203
|
+
['log', '--pretty=format:commit:%H%n%at%n%ae%x28%an%x29', '--name-status', `${this.repoPublishBranch}..${this.repoBranch}`],
|
|
204
|
+
this.repoDir
|
|
205
|
+
);
|
|
206
|
+
this.logger.debug('[git] Changes check done');
|
|
207
|
+
return logResult.stdout
|
|
208
|
+
.split('commit:')
|
|
209
|
+
.filter(Boolean)
|
|
210
|
+
.map((rawCommit) => {
|
|
211
|
+
const split = rawCommit.trim().split('\n');
|
|
212
|
+
return {
|
|
213
|
+
author: this.parseGitCommitAuthor(split[2]),
|
|
214
|
+
timestamp: split[1] ? new Date(parseInt(split[1]) * 1000) : undefined,
|
|
215
|
+
commitHash: split[0],
|
|
216
|
+
changes: split
|
|
217
|
+
.slice(3)
|
|
218
|
+
.map((line) => line.trim().split(/\t/))
|
|
219
|
+
.filter(Boolean)
|
|
220
|
+
.filter(([status, filename]) => status && filename)
|
|
221
|
+
.map(([status, filename]) => [GIT_LOG_CHANGE_TYPES[status!] || 'modified', filename])
|
|
222
|
+
};
|
|
223
|
+
})
|
|
224
|
+
.reverse();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import childProcess, { ChildProcessWithoutNullStreams } from 'child_process';
|
|
2
|
+
import { RunServiceInterface } from '@stackbit/types';
|
|
3
|
+
|
|
4
|
+
export class RunService implements RunServiceInterface {
|
|
5
|
+
private readonly env: NodeJS.ProcessEnv;
|
|
6
|
+
private readonly uid?: number;
|
|
7
|
+
|
|
8
|
+
constructor(options: { env: NodeJS.ProcessEnv; uid?: number }) {
|
|
9
|
+
this.env = options.env;
|
|
10
|
+
this.uid = options.uid;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
command(command: string, args: string[], cwd?: string, shell?: boolean) {
|
|
14
|
+
return getProcessPromise(
|
|
15
|
+
childProcess.spawn(command, args, {
|
|
16
|
+
cwd: cwd,
|
|
17
|
+
shell: shell,
|
|
18
|
+
env: this.env,
|
|
19
|
+
...(this.uid ? { uid: this.uid } : {})
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getProcessPromise(p: ChildProcessWithoutNullStreams): Promise<{
|
|
26
|
+
stdout: string;
|
|
27
|
+
stderr: string;
|
|
28
|
+
exitCode?: number;
|
|
29
|
+
err?: Error;
|
|
30
|
+
}> {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let stdout = '';
|
|
33
|
+
let stderr = '';
|
|
34
|
+
p.stdout.on('data', (out) => (stdout += out));
|
|
35
|
+
p.stderr.on('data', (out) => (stderr += out));
|
|
36
|
+
p.on('exit', (exitCode) => {
|
|
37
|
+
if (exitCode !== 0) {
|
|
38
|
+
reject({
|
|
39
|
+
err: new Error(`process exited with code: ${exitCode}, stderr: ${stderr}`),
|
|
40
|
+
stdout,
|
|
41
|
+
stderr,
|
|
42
|
+
exitCode
|
|
43
|
+
});
|
|
44
|
+
} else {
|
|
45
|
+
resolve({
|
|
46
|
+
stdout,
|
|
47
|
+
stderr
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
p.on('error', (err) => {
|
|
52
|
+
reject({
|
|
53
|
+
err,
|
|
54
|
+
stdout,
|
|
55
|
+
stderr
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -111,19 +111,32 @@ export async function createDocumentRecursively({
|
|
|
111
111
|
if (!model || !csiModel) {
|
|
112
112
|
throw new Error(`no model with name '${modelName}' was found`);
|
|
113
113
|
}
|
|
114
|
+
const modelFields = model.fields ?? [];
|
|
115
|
+
const csiModelFields = csiModel.fields ?? [];
|
|
114
116
|
if (model.type === 'page') {
|
|
115
117
|
const tokens = extractTokensFromString(String(model.urlPath));
|
|
116
118
|
const slugField = _.last(tokens);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
//TODO legacy
|
|
120
|
+
if (object && slugField && (slugField in object || '_stackbit_slug' in object)) {
|
|
121
|
+
const slugFieldValue = object[slugField] || object['_stackbit_slug'];
|
|
119
122
|
object[slugField] = sanitizeSlug(slugFieldValue);
|
|
123
|
+
if (!modelFields.find(field => field.name === slugField)) {
|
|
124
|
+
modelFields.push({
|
|
125
|
+
type: 'slug',
|
|
126
|
+
name: slugField
|
|
127
|
+
});
|
|
128
|
+
csiModelFields.push({
|
|
129
|
+
type: 'slug',
|
|
130
|
+
name: slugField
|
|
131
|
+
});
|
|
132
|
+
}
|
|
120
133
|
}
|
|
121
134
|
}
|
|
122
135
|
|
|
123
136
|
const nestedResult = await createObjectRecursively({
|
|
124
137
|
object,
|
|
125
|
-
modelFields
|
|
126
|
-
csiModelFields
|
|
138
|
+
modelFields,
|
|
139
|
+
csiModelFields,
|
|
127
140
|
fieldPath: [modelName],
|
|
128
141
|
modelMap,
|
|
129
142
|
csiModelMap,
|
|
@@ -324,7 +337,7 @@ async function createUpdateOperationFieldRecursively({
|
|
|
324
337
|
return {
|
|
325
338
|
field: {
|
|
326
339
|
type: 'image',
|
|
327
|
-
value: _.omit(value, ['$$type'])
|
|
340
|
+
value: _.omit(value, ['$$type'])
|
|
328
341
|
},
|
|
329
342
|
newRefDocuments: []
|
|
330
343
|
}
|
|
@@ -110,16 +110,9 @@ function mergeObjectWithDocumentField({
|
|
|
110
110
|
}
|
|
111
111
|
break;
|
|
112
112
|
}
|
|
113
|
-
case 'image': {
|
|
114
|
-
if (typeof value !== 'undefined') {
|
|
115
|
-
return value;
|
|
116
|
-
}
|
|
113
|
+
case 'image': {
|
|
117
114
|
const localizedField = getDocumentFieldForLocale(documentField, locale);
|
|
118
115
|
if (localizedField && !localizedField.isUnset && isPlainObjectOrUndefined(value)) {
|
|
119
|
-
if (localizedField?.sourceData) {
|
|
120
|
-
return localizedField?.sourceData;
|
|
121
|
-
}
|
|
122
|
-
//TODO needs testing, looks like we need to use the url field instead of this
|
|
123
116
|
return mergeObjectWithDocumentFields({
|
|
124
117
|
object: value,
|
|
125
118
|
documentFields: localizedField.fields,
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
+
import path from 'path';
|
|
2
3
|
import { omitByNil } from '@stackbit/utils';
|
|
3
4
|
import * as ContentStoreTypes from '../content-store-types';
|
|
4
5
|
|
|
5
|
-
export function mapDocumentsToLocalizedApiObjects(documents: ContentStoreTypes.Document[], locale?: string): ContentStoreTypes.APIDocumentObject[] {
|
|
6
|
-
return documents.map((document) => documentToLocalizedApiObject(document, locale));
|
|
6
|
+
export function mapDocumentsToLocalizedApiObjects(documents: ContentStoreTypes.Document[], staticAssetsPublicPath: string, locale?: string): ContentStoreTypes.APIDocumentObject[] {
|
|
7
|
+
return documents.map((document) => documentToLocalizedApiObject(document, staticAssetsPublicPath, locale));
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
function documentToLocalizedApiObject(document: ContentStoreTypes.Document, locale?: string): ContentStoreTypes.APIDocumentObject {
|
|
10
|
+
function documentToLocalizedApiObject(document: ContentStoreTypes.Document, staticAssetsPublicPath: string, locale?: string): ContentStoreTypes.APIDocumentObject {
|
|
10
11
|
const { type, fields, ...rest } = document;
|
|
11
12
|
return {
|
|
12
13
|
type: 'object',
|
|
13
14
|
...rest,
|
|
14
|
-
fields: toLocalizedAPIFields(fields, locale)
|
|
15
|
+
fields: toLocalizedAPIFields(fields, staticAssetsPublicPath, locale)
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
function toLocalizedAPIFields(docFields: Record<string, ContentStoreTypes.DocumentField>, locale?: string): Record<string, ContentStoreTypes.DocumentFieldAPI> {
|
|
19
|
-
return _.mapValues(docFields, (docField) => toLocalizedAPIField(docField, locale));
|
|
19
|
+
function toLocalizedAPIFields(docFields: Record<string, ContentStoreTypes.DocumentField>, staticAssetsPublicPath: string, locale?: string): Record<string, ContentStoreTypes.DocumentFieldAPI> {
|
|
20
|
+
return _.mapValues(docFields, (docField) => toLocalizedAPIField(docField, staticAssetsPublicPath, locale));
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?: string, isListItem = false): ContentStoreTypes.DocumentFieldAPI {
|
|
23
|
+
function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, staticAssetsPublicPath: string, locale?: string, isListItem = false): ContentStoreTypes.DocumentFieldAPI {
|
|
23
24
|
type ToBoolean<T extends boolean | undefined> = T extends true ? true : false;
|
|
24
25
|
function localeFields<T extends boolean | undefined>(localized: T): null | { localized: false } | { localized: true; locale: string } {
|
|
25
26
|
const isLocalized = !!localized as ToBoolean<T>;
|
|
@@ -71,33 +72,43 @@ function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?:
|
|
|
71
72
|
...localeFields(docField.localized)
|
|
72
73
|
};
|
|
73
74
|
case 'image':
|
|
75
|
+
let result: ContentStoreTypes.DocumentFieldAPI;
|
|
74
76
|
if (docField.localized) {
|
|
75
77
|
const { localized, locales, ...base } = docField;
|
|
76
78
|
const localeProps = locales && locale ? locales[locale] : undefined;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
if (localeProps) {
|
|
80
|
+
const fields = toLocalizedAPIFields(localeProps.fields, staticAssetsPublicPath, locale) as ContentStoreTypes.ImageFieldsAPI;
|
|
81
|
+
result = {
|
|
82
|
+
...base,
|
|
83
|
+
...localeProps,
|
|
84
|
+
fields,
|
|
85
|
+
...localeFields(localized)
|
|
86
|
+
};
|
|
87
|
+
} else {
|
|
88
|
+
result = {
|
|
89
|
+
...base,
|
|
90
|
+
isUnset: true,
|
|
91
|
+
...localeFields(localized)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
if (docField.isUnset) {
|
|
96
|
+
result = {
|
|
97
|
+
...docField,
|
|
98
|
+
type: 'image',
|
|
99
|
+
...localeFields(docField.localized)
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
const fields = toLocalizedAPIFields(docField.fields, staticAssetsPublicPath, locale) as ContentStoreTypes.ImageFieldsAPI;
|
|
103
|
+
result = {
|
|
104
|
+
...docField,
|
|
105
|
+
type: 'image',
|
|
106
|
+
fields,
|
|
107
|
+
...localeFields(docField.localized)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
87
110
|
}
|
|
88
|
-
return
|
|
89
|
-
...(!docField.isUnset
|
|
90
|
-
? {
|
|
91
|
-
...docField,
|
|
92
|
-
type: 'image',
|
|
93
|
-
fields: toLocalizedAPIFields(docField.fields, locale) as ContentStoreTypes.ImageFieldsAPI
|
|
94
|
-
}
|
|
95
|
-
: {
|
|
96
|
-
...docField,
|
|
97
|
-
type: 'image'
|
|
98
|
-
}),
|
|
99
|
-
...localeFields(docField.localized)
|
|
100
|
-
};
|
|
111
|
+
return result;
|
|
101
112
|
case 'object':
|
|
102
113
|
case 'model':
|
|
103
114
|
if (docField.localized) {
|
|
@@ -109,7 +120,7 @@ function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?:
|
|
|
109
120
|
...(localeProps
|
|
110
121
|
? {
|
|
111
122
|
...localeProps,
|
|
112
|
-
fields: toLocalizedAPIFields(localeProps.fields, locale)
|
|
123
|
+
fields: toLocalizedAPIFields(localeProps.fields, staticAssetsPublicPath, locale)
|
|
113
124
|
}
|
|
114
125
|
: { isUnset: true }),
|
|
115
126
|
...localeFields(localized)
|
|
@@ -123,7 +134,7 @@ function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?:
|
|
|
123
134
|
...(localeProps
|
|
124
135
|
? {
|
|
125
136
|
...localeProps,
|
|
126
|
-
fields: toLocalizedAPIFields(localeProps.fields, locale)
|
|
137
|
+
fields: toLocalizedAPIFields(localeProps.fields, staticAssetsPublicPath, locale)
|
|
127
138
|
}
|
|
128
139
|
: { isUnset: true }),
|
|
129
140
|
...localeFields(localized)
|
|
@@ -135,7 +146,7 @@ function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?:
|
|
|
135
146
|
? {
|
|
136
147
|
...docField,
|
|
137
148
|
type: 'object',
|
|
138
|
-
fields: toLocalizedAPIFields(docField.fields, locale)
|
|
149
|
+
fields: toLocalizedAPIFields(docField.fields, staticAssetsPublicPath, locale)
|
|
139
150
|
}
|
|
140
151
|
: {
|
|
141
152
|
...docField,
|
|
@@ -222,14 +233,14 @@ function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?:
|
|
|
222
233
|
return {
|
|
223
234
|
...base,
|
|
224
235
|
...localeProps,
|
|
225
|
-
items: (localeProps?.items ?? []).map((field) => toLocalizedAPIField(field, locale, true) as ContentStoreTypes.DocumentListFieldItemsAPI),
|
|
236
|
+
items: (localeProps?.items ?? []).map((field) => toLocalizedAPIField(field, staticAssetsPublicPath, locale, true) as ContentStoreTypes.DocumentListFieldItemsAPI),
|
|
226
237
|
...localeFields(localized)
|
|
227
238
|
};
|
|
228
239
|
}
|
|
229
240
|
return {
|
|
230
241
|
...docField,
|
|
231
242
|
...localeFields(docField.localized),
|
|
232
|
-
items: docField.items.map((field) => toLocalizedAPIField(field, locale, true) as ContentStoreTypes.DocumentListFieldItemsAPI)
|
|
243
|
+
items: docField.items.map((field) => toLocalizedAPIField(field, staticAssetsPublicPath, locale, true) as ContentStoreTypes.DocumentListFieldItemsAPI)
|
|
233
244
|
};
|
|
234
245
|
default:
|
|
235
246
|
const _exhaustiveCheck: never = docField;
|
|
@@ -238,20 +249,20 @@ function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?:
|
|
|
238
249
|
}
|
|
239
250
|
}
|
|
240
251
|
|
|
241
|
-
export function mapAssetsToLocalizedApiImages(assets: ContentStoreTypes.Asset[], locale?: string): ContentStoreTypes.APIImageObject[] {
|
|
242
|
-
return assets.map((asset) => assetToLocalizedApiImage(asset, locale));
|
|
252
|
+
export function mapAssetsToLocalizedApiImages(assets: ContentStoreTypes.Asset[], staticAssetsPublicPath: string, locale?: string): ContentStoreTypes.APIImageObject[] {
|
|
253
|
+
return assets.map((asset) => assetToLocalizedApiImage(asset, staticAssetsPublicPath, locale));
|
|
243
254
|
}
|
|
244
255
|
|
|
245
|
-
function assetToLocalizedApiImage(asset: ContentStoreTypes.Asset, locale?: string): ContentStoreTypes.APIImageObject {
|
|
256
|
+
function assetToLocalizedApiImage(asset: ContentStoreTypes.Asset, staticAssetsPublicPath: string, locale?: string): ContentStoreTypes.APIImageObject {
|
|
246
257
|
const { type, fields, ...rest } = asset;
|
|
247
258
|
return {
|
|
248
259
|
type: 'image',
|
|
249
260
|
...rest,
|
|
250
|
-
fields: localizeAssetFields(fields, locale)
|
|
261
|
+
fields: localizeAssetFields(fields, staticAssetsPublicPath, locale)
|
|
251
262
|
};
|
|
252
263
|
}
|
|
253
264
|
|
|
254
|
-
function localizeAssetFields(assetFields: ContentStoreTypes.AssetFields, locale?: string): ContentStoreTypes.AssetFieldsAPI {
|
|
265
|
+
function localizeAssetFields(assetFields: ContentStoreTypes.AssetFields, staticAssetsPublicPath: string, locale?: string): ContentStoreTypes.AssetFieldsAPI {
|
|
255
266
|
const fields: ContentStoreTypes.AssetFieldsAPI = {
|
|
256
267
|
title: {
|
|
257
268
|
type: 'string' as const,
|
|
@@ -285,11 +296,11 @@ function localizeAssetFields(assetFields: ContentStoreTypes.AssetFields, locale?
|
|
|
285
296
|
return fields;
|
|
286
297
|
}
|
|
287
298
|
|
|
288
|
-
export function mapStoreAssetsToAPIAssets(assets: ContentStoreTypes.Asset[], locale?: string): ContentStoreTypes.APIAsset[] {
|
|
289
|
-
return assets.map((asset) => storeAssetToAPIAsset(asset, locale)).filter((asset): asset is ContentStoreTypes.APIAsset => !!asset);
|
|
299
|
+
export function mapStoreAssetsToAPIAssets(assets: ContentStoreTypes.Asset[], staticAssetsPublicPath: string, locale?: string): ContentStoreTypes.APIAsset[] {
|
|
300
|
+
return assets.map((asset) => storeAssetToAPIAsset(asset, staticAssetsPublicPath, locale)).filter((asset): asset is ContentStoreTypes.APIAsset => !!asset);
|
|
290
301
|
}
|
|
291
302
|
|
|
292
|
-
function storeAssetToAPIAsset(asset: ContentStoreTypes.Asset, locale?: string): ContentStoreTypes.APIAsset | null {
|
|
303
|
+
function storeAssetToAPIAsset(asset: ContentStoreTypes.Asset, staticAssetsPublicPath: string, locale?: string): ContentStoreTypes.APIAsset | null {
|
|
293
304
|
const assetTitleField = asset.fields.title;
|
|
294
305
|
const localizedTitleField = assetTitleField.localized ? assetTitleField.locales?.[locale!] : assetTitleField;
|
|
295
306
|
const assetFileField = asset.fields.file;
|
|
@@ -300,7 +311,7 @@ function storeAssetToAPIAsset(asset: ContentStoreTypes.Asset, locale?: string):
|
|
|
300
311
|
return {
|
|
301
312
|
objectId: asset.srcObjectId,
|
|
302
313
|
createdAt: asset.createdAt,
|
|
303
|
-
url: localizedFileField.url,
|
|
314
|
+
url: replaceAssetUrlIfNeeded(staticAssetsPublicPath, localizedFileField.url) ?? staticAssetsPublicPath,
|
|
304
315
|
...omitByNil({
|
|
305
316
|
title: localizedTitleField?.value ?? null,
|
|
306
317
|
fileName: localizedFileField.fileName,
|
|
@@ -311,3 +322,11 @@ function storeAssetToAPIAsset(asset: ContentStoreTypes.Asset, locale?: string):
|
|
|
311
322
|
})
|
|
312
323
|
};
|
|
313
324
|
}
|
|
325
|
+
|
|
326
|
+
function replaceAssetUrlIfNeeded(staticAssetsPublicPath: string, value: string | undefined) {
|
|
327
|
+
let url = value;
|
|
328
|
+
if (url && !url.startsWith('http:') && !url.startsWith('https:')) {
|
|
329
|
+
url = path.join(staticAssetsPublicPath, url);
|
|
330
|
+
}
|
|
331
|
+
return url;
|
|
332
|
+
}
|
|
@@ -95,8 +95,6 @@ function mapStoreFieldToCSIField(documentField: ContentStoreTypes.DocumentField)
|
|
|
95
95
|
return {
|
|
96
96
|
type: 'image',
|
|
97
97
|
localized: true,
|
|
98
|
-
source: documentField.source,
|
|
99
|
-
sourceData: documentField.sourceData,
|
|
100
98
|
locales: _.mapValues(documentField.locales, (locale) => ({
|
|
101
99
|
locale: locale.locale,
|
|
102
100
|
fields: mapStoreFieldsToCSIFields(locale.fields)
|
|
@@ -108,8 +106,6 @@ function mapStoreFieldToCSIField(documentField: ContentStoreTypes.DocumentField)
|
|
|
108
106
|
}
|
|
109
107
|
return {
|
|
110
108
|
type: 'image',
|
|
111
|
-
source: documentField.source,
|
|
112
|
-
sourceData: documentField.sourceData,
|
|
113
109
|
fields: mapStoreFieldsToCSIFields(documentField.fields)
|
|
114
110
|
} as CSITypes.DocumentImageFieldNonLocalized;
|
|
115
111
|
}
|