@stackbit/cms-core 0.1.25-cloudinary-presets.0 → 0.1.26-develop.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-utils.d.ts +5 -5
- package/dist/content-store-utils.d.ts.map +1 -1
- package/dist/content-store-utils.js +15 -3
- package/dist/content-store-utils.js.map +1 -1
- package/dist/content-store.d.ts +11 -3
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +10 -5
- package/dist/content-store.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/services/git.d.ts +38 -0
- package/dist/services/git.d.ts.map +1 -0
- package/dist/services/git.js +201 -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 +7 -0
- package/dist/services/run.d.ts.map +1 -0
- package/dist/services/run.js +53 -0
- package/dist/services/run.js.map +1 -0
- package/dist/types/content-store-document-fields.d.ts +600 -0
- package/dist/types/content-store-document-fields.d.ts.map +1 -0
- package/dist/types/content-store-document-fields.js +3 -0
- package/dist/types/content-store-document-fields.js.map +1 -0
- package/dist/types/content-store-documents.d.ts +99 -0
- package/dist/types/content-store-documents.d.ts.map +1 -0
- package/dist/types/content-store-documents.js +3 -0
- package/dist/types/content-store-documents.js.map +1 -0
- package/dist/types/content-store-types.d.ts +75 -0
- package/dist/types/content-store-types.d.ts.map +1 -0
- package/dist/types/content-store-types.js.map +1 -0
- package/dist/types/content-store-update-operation.d.ts +61 -0
- package/dist/types/content-store-update-operation.d.ts.map +1 -0
- package/dist/types/content-store-update-operation.js +3 -0
- package/dist/types/content-store-update-operation.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/search-filter.d.ts +1 -1
- package/dist/types/search-filter.d.ts.map +1 -1
- package/dist/utils/create-update-csi-docs.d.ts +1 -1
- package/dist/utils/create-update-csi-docs.d.ts.map +1 -1
- package/dist/utils/create-update-csi-docs.js +27 -13
- package/dist/utils/create-update-csi-docs.js.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.d.ts +1 -1
- package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.js +20 -4
- package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
- package/dist/utils/duplicate-document.d.ts +1 -1
- package/dist/utils/duplicate-document.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/search-utils.d.ts +1 -1
- package/dist/utils/search-utils.d.ts.map +1 -1
- package/dist/utils/search-utils.js +16 -16
- package/dist/utils/search-utils.js.map +1 -1
- package/dist/utils/site-map.d.ts +1 -1
- package/dist/utils/site-map.d.ts.map +1 -1
- package/dist/utils/site-map.js +9 -0
- package/dist/utils/site-map.js.map +1 -1
- package/dist/utils/store-to-api-docs-converter.d.ts +2 -2
- package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
- package/dist/utils/store-to-api-docs-converter.js +87 -38
- package/dist/utils/store-to-api-docs-converter.js.map +1 -1
- package/dist/utils/store-to-csi-docs-converter.d.ts +1 -1
- package/dist/utils/store-to-csi-docs-converter.d.ts.map +1 -1
- package/dist/utils/timer.d.ts +1 -1
- package/dist/utils/timer.d.ts.map +1 -1
- package/package.json +9 -6
- package/src/content-store-utils.ts +19 -8
- package/src/content-store.ts +28 -15
- package/src/index.ts +2 -1
- package/src/services/git.ts +245 -0
- package/src/services/index.ts +2 -0
- package/src/services/run.ts +54 -0
- package/src/types/content-store-document-fields.ts +658 -0
- package/src/types/content-store-documents.ts +113 -0
- package/src/types/content-store-types.ts +96 -0
- package/src/types/content-store-update-operation.ts +85 -0
- package/src/types/index.ts +5 -0
- package/src/types/search-filter.ts +26 -19
- package/src/utils/create-update-csi-docs.ts +32 -15
- package/src/utils/csi-to-store-docs-converter.ts +33 -14
- package/src/utils/duplicate-document.ts +2 -2
- package/src/utils/search-utils.ts +18 -19
- package/src/utils/site-map.ts +10 -1
- package/src/utils/store-to-api-docs-converter.ts +86 -38
- package/src/utils/store-to-csi-docs-converter.ts +1 -1
- package/src/utils/timer.ts +1 -1
- package/dist/content-store-types.d.ts +0 -413
- package/dist/content-store-types.d.ts.map +0 -1
- package/dist/content-store-types.js.map +0 -1
- package/src/content-store-types.ts +0 -531
- /package/dist/{content-store-types.js → types/content-store-types.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as CSITypes from '@stackbit/types';
|
|
2
|
-
import * as ContentStoreTypes from '../
|
|
2
|
+
import * as ContentStoreTypes from '../types';
|
|
3
3
|
export declare function mapStoreDocumentsToCSIDocumentsWithSource(documents: ContentStoreTypes.Document[]): CSITypes.DocumentWithSource[];
|
|
4
4
|
//# sourceMappingURL=store-to-csi-docs-converter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-to-csi-docs-converter.d.ts","sourceRoot":"","sources":["../../src/utils/store-to-csi-docs-converter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,iBAAiB,MAAM,
|
|
1
|
+
{"version":3,"file":"store-to-csi-docs-converter.d.ts","sourceRoot":"","sources":["../../src/utils/store-to-csi-docs-converter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,iBAAiB,MAAM,UAAU,CAAC;AAE9C,wBAAgB,yCAAyC,CAAC,SAAS,EAAE,iBAAiB,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,kBAAkB,EAAE,CAEhI"}
|
package/dist/utils/timer.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timer.d.ts","sourceRoot":"","sources":["../../src/utils/timer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"timer.d.ts","sourceRoot":"","sources":["../../src/utils/timer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,qBAAa,KAAK;IACd,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAa;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,OAAO,CAAwB;gBAE3B,EAAE,aAAa,EAAE,OAAwB,EAAE,MAAM,EAAE,EAAE;QAAE,aAAa,EAAE,MAAM,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAQjI,SAAS;IAIT,UAAU;IAIV,UAAU;IAKV,SAAS;IAOT,aAAa;CAKhB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackbit/cms-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26-develop.0",
|
|
4
4
|
"description": "stackbit-dev",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
"author": "Stackbit Inc.",
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"devDependencies": {
|
|
21
|
+
"@types/fs-extra": "^11.0.1",
|
|
21
22
|
"@types/jest": "^27.5.2",
|
|
22
23
|
"@types/lodash": "^4.14.182",
|
|
24
|
+
"@types/uuid": "^9.0.0",
|
|
23
25
|
"jest": "^27.4.7",
|
|
24
26
|
"prettier": "^2.5.1",
|
|
25
27
|
"ts-jest": "^27.1.3",
|
|
@@ -29,9 +31,9 @@
|
|
|
29
31
|
"@babel/parser": "^7.11.5",
|
|
30
32
|
"@babel/traverse": "^7.11.5",
|
|
31
33
|
"@iarna/toml": "^2.2.3",
|
|
32
|
-
"@stackbit/sdk": "
|
|
33
|
-
"@stackbit/types": "
|
|
34
|
-
"@stackbit/utils": "
|
|
34
|
+
"@stackbit/sdk": "0.3.19-develop.0",
|
|
35
|
+
"@stackbit/types": "0.1.11-develop.0",
|
|
36
|
+
"@stackbit/utils": "0.2.18-develop.0",
|
|
35
37
|
"chalk": "^4.0.1",
|
|
36
38
|
"esm": "^3.2.25",
|
|
37
39
|
"fs-extra": "^8.1.0",
|
|
@@ -43,7 +45,8 @@
|
|
|
43
45
|
"moment": "^2.29.1",
|
|
44
46
|
"parse5": "^6.0.1",
|
|
45
47
|
"sanitize-filename": "^1.6.3",
|
|
46
|
-
"slugify": "^1.6.5"
|
|
48
|
+
"slugify": "^1.6.5",
|
|
49
|
+
"uuid": "^9.0.0"
|
|
47
50
|
},
|
|
48
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "d2032e3174de3c2c1b853198d3a2601a6482b68c"
|
|
49
52
|
}
|
|
@@ -3,8 +3,7 @@ import { Model } from '@stackbit/sdk';
|
|
|
3
3
|
import { append } from '@stackbit/utils';
|
|
4
4
|
import { Field, FieldList, FieldListItems, FieldListProps, FieldObjectProps, FieldSpecificProps, FieldType, UpdateOperationValueField } from '@stackbit/types';
|
|
5
5
|
import * as CSITypes from '@stackbit/types';
|
|
6
|
-
import * as ContentStoreTypes from './
|
|
7
|
-
import { CrossReferenceData } from './content-store-types';
|
|
6
|
+
import * as ContentStoreTypes from './types';
|
|
8
7
|
|
|
9
8
|
export function getContentSourceIdForContentSource(contentSource: CSITypes.ContentSourceInterface): string {
|
|
10
9
|
return getContentSourceId(contentSource.getContentSourceType(), contentSource.getProjectId());
|
|
@@ -15,10 +14,22 @@ export function getContentSourceId(contentSourceType: string, srcProjectId: stri
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
export function getUserContextForSrcType(srcType: string, user?: ContentStoreTypes.User): unknown {
|
|
18
|
-
|
|
17
|
+
if (!user) {
|
|
18
|
+
return user;
|
|
19
|
+
}
|
|
20
|
+
const connection = user?.connections?.find((connection) => connection.type === srcType);
|
|
21
|
+
return {
|
|
22
|
+
email: user.email,
|
|
23
|
+
name: user.name,
|
|
24
|
+
...connection
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isDocumentFieldOneOfFieldTypes<T extends FieldType>(documentField: ContentStoreTypes.DocumentField, fieldTypes: ReadonlyArray<T>): documentField is ContentStoreTypes.DocumentFieldForType<T> {
|
|
29
|
+
return fieldTypes.includes(documentField.type as T);
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
export function getDocumentFieldForLocale<Type extends
|
|
32
|
+
export function getDocumentFieldForLocale<Type extends FieldType>(
|
|
22
33
|
docField: ContentStoreTypes.DocumentFieldForType<Type>,
|
|
23
34
|
locale?: string
|
|
24
35
|
): ContentStoreTypes.DocumentFieldNonLocalizedForType<Type> | null {
|
|
@@ -31,10 +42,10 @@ export function getDocumentFieldForLocale<Type extends ContentStoreTypes.FieldTy
|
|
|
31
42
|
if (!localizedField) {
|
|
32
43
|
return null;
|
|
33
44
|
}
|
|
34
|
-
return
|
|
45
|
+
return {
|
|
35
46
|
...base,
|
|
36
47
|
...localizedField
|
|
37
|
-
} as unknown
|
|
48
|
+
} as unknown as ContentStoreTypes.DocumentFieldNonLocalizedForType<Type>;
|
|
38
49
|
} else {
|
|
39
50
|
return docField;
|
|
40
51
|
}
|
|
@@ -194,9 +205,9 @@ export function getCSIDocumentsAndAssetsFromContentSourceDataByIds(
|
|
|
194
205
|
};
|
|
195
206
|
}
|
|
196
207
|
|
|
197
|
-
export function updateOperationValueFieldWithCrossReference(type: 'string' | 'text' | 'json', refObject: CrossReferenceData): UpdateOperationValueField {
|
|
208
|
+
export function updateOperationValueFieldWithCrossReference(type: 'string' | 'text' | 'json' | 'cross-reference', refObject: ContentStoreTypes.CrossReferenceData): UpdateOperationValueField {
|
|
198
209
|
return {
|
|
199
210
|
type,
|
|
200
|
-
value: type === 'json' ? refObject : JSON.stringify(refObject)
|
|
211
|
+
value: (type === 'json' || type === 'cross-reference') ? refObject : JSON.stringify(refObject)
|
|
201
212
|
};
|
|
202
213
|
}
|
package/src/content-store.ts
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import sanitizeFilename from 'sanitize-filename';
|
|
4
4
|
|
|
5
5
|
import * as CSITypes from '@stackbit/types';
|
|
6
|
-
import { getLocalizedFieldForLocale, ModelExtension, UserCommandSpawner } from '@stackbit/types';
|
|
6
|
+
import { getLocalizedFieldForLocale, isOneOfFieldTypes, ModelExtension, UserCommandSpawner } from '@stackbit/types';
|
|
7
7
|
import {
|
|
8
8
|
Config,
|
|
9
9
|
extendModelsWithPresetsIds,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from '@stackbit/sdk';
|
|
21
21
|
import { append, deferWhileRunning, mapPromise, reducePromise } from '@stackbit/utils';
|
|
22
22
|
|
|
23
|
-
import * as ContentStoreTypes from './
|
|
23
|
+
import * as ContentStoreTypes from './types';
|
|
24
24
|
import { Timer } from './utils/timer';
|
|
25
25
|
import { SearchFilter } from './types/search-filter';
|
|
26
26
|
import { searchDocuments } from './utils/search-utils';
|
|
@@ -48,15 +48,20 @@ import { mergeObjectWithDocument } from './utils/duplicate-document';
|
|
|
48
48
|
import { normalizeModels, validateModels } from './utils/model-utils';
|
|
49
49
|
import { IMAGE_MODEL } from './common/common-schema';
|
|
50
50
|
import { getDocumentObjectFromPreset, getPresetFromDocument } from './utils/preset-utils';
|
|
51
|
+
import { GitService } from './services';
|
|
52
|
+
import { CommandRunner } from '@stackbit/types';
|
|
51
53
|
|
|
52
54
|
export type HandleConfigAssets = <T extends Model>({ models, presets }: { models?: T[]; presets?: PresetMap }) => Promise<{ models: T[]; presets: PresetMap }>;
|
|
53
55
|
|
|
54
56
|
export interface ContentSourceOptions {
|
|
55
|
-
logger:
|
|
56
|
-
userLogger:
|
|
57
|
+
logger: CSITypes.Logger;
|
|
58
|
+
userLogger: CSITypes.Logger;
|
|
57
59
|
localDev: boolean;
|
|
60
|
+
staticAssetsPublicPath: string;
|
|
58
61
|
webhookUrl?: string;
|
|
59
|
-
|
|
62
|
+
runCommand: CommandRunner;
|
|
63
|
+
git: GitService;
|
|
64
|
+
userCommandSpawner?: UserCommandSpawner; //TODO remove
|
|
60
65
|
onSchemaChangeCallback: () => void;
|
|
61
66
|
onContentChangeCallback: (contentChanges: ContentStoreTypes.ContentChangeResult) => void;
|
|
62
67
|
handleConfigAssets: HandleConfigAssets;
|
|
@@ -69,11 +74,14 @@ type ContentSourceRawData = Omit<ContentSourceData, 'models' | 'modelMap' | 'doc
|
|
|
69
74
|
export const StackbitPresetModelName = 'stackbitPreset';
|
|
70
75
|
|
|
71
76
|
export class ContentStore {
|
|
72
|
-
private readonly logger:
|
|
73
|
-
private readonly userLogger:
|
|
77
|
+
private readonly logger: CSITypes.Logger;
|
|
78
|
+
private readonly userLogger: CSITypes.Logger;
|
|
74
79
|
private readonly userCommandSpawner?: UserCommandSpawner;
|
|
75
80
|
private readonly localDev: boolean;
|
|
81
|
+
private readonly staticAssetsPublicPath: string;
|
|
76
82
|
private readonly webhookUrl?: string;
|
|
83
|
+
private readonly runCommand: CommandRunner;
|
|
84
|
+
private readonly git: GitService;
|
|
77
85
|
private readonly onSchemaChangeCallback: () => void;
|
|
78
86
|
private readonly onContentChangeCallback: (contentChanges: ContentStoreTypes.ContentChangeResult) => void;
|
|
79
87
|
private readonly handleConfigAssets: HandleConfigAssets;
|
|
@@ -93,7 +101,10 @@ export class ContentStore {
|
|
|
93
101
|
this.logger = options.logger.createLogger({ label: 'content-store' });
|
|
94
102
|
this.userLogger = options.userLogger.createLogger({ label: 'content-store' });
|
|
95
103
|
this.localDev = options.localDev;
|
|
104
|
+
this.staticAssetsPublicPath = options.staticAssetsPublicPath;
|
|
96
105
|
this.webhookUrl = options.webhookUrl;
|
|
106
|
+
this.runCommand = options.runCommand;
|
|
107
|
+
this.git = options.git;
|
|
97
108
|
this.userCommandSpawner = options.userCommandSpawner;
|
|
98
109
|
this.onSchemaChangeCallback = options.onSchemaChangeCallback;
|
|
99
110
|
this.onContentChangeCallback = options.onContentChangeCallback;
|
|
@@ -430,7 +441,9 @@ export class ContentStore {
|
|
|
430
441
|
userCommandSpawner: this.userCommandSpawner,
|
|
431
442
|
localDev: this.localDev,
|
|
432
443
|
webhookUrl: this.getWebhookUrl(contentSourceInstance.getContentSourceType(), contentSourceInstance.getProjectId()),
|
|
433
|
-
devAppRestartNeeded: this.devAppRestartNeeded
|
|
444
|
+
devAppRestartNeeded: this.devAppRestartNeeded,
|
|
445
|
+
runCommand: this.runCommand,
|
|
446
|
+
git: this.git
|
|
434
447
|
});
|
|
435
448
|
} else {
|
|
436
449
|
contentSourceInstance.stopWatchingContentUpdates();
|
|
@@ -481,7 +494,7 @@ export class ContentStore {
|
|
|
481
494
|
}
|
|
482
495
|
this.logger.debug('content source called onContentChange.', { contentSourceId });
|
|
483
496
|
const result = this.onContentChange(contentSourceId, contentChangeEvent);
|
|
484
|
-
|
|
497
|
+
|
|
485
498
|
this.siteMapEntryGroups = await updateSiteMapEntriesWithContentChanges({
|
|
486
499
|
siteMapEntryGroups: this.siteMapEntryGroups,
|
|
487
500
|
contentChanges: result,
|
|
@@ -1116,7 +1129,7 @@ export class ContentStore {
|
|
|
1116
1129
|
return _.reduce(
|
|
1117
1130
|
this.contentSourceDataById,
|
|
1118
1131
|
(objects: ContentStoreTypes.APIObject[], contentSourceData) => {
|
|
1119
|
-
let documents = objectIds
|
|
1132
|
+
let documents = objectIds
|
|
1120
1133
|
? contentSourceData.documents.filter((document) => objectIds.includes(document.srcObjectId))
|
|
1121
1134
|
: contentSourceData.documents;
|
|
1122
1135
|
documents = hasExplicitLocale
|
|
@@ -1154,12 +1167,12 @@ export class ContentStore {
|
|
|
1154
1167
|
if (srcProjectId && srcType) {
|
|
1155
1168
|
const contentSourceId = getContentSourceId(srcType, srcProjectId);
|
|
1156
1169
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
1157
|
-
assets = mapStoreAssetsToAPIAssets(contentSourceData.assets, contentSourceData.defaultLocaleCode);
|
|
1170
|
+
assets = mapStoreAssetsToAPIAssets(contentSourceData.assets, this.staticAssetsPublicPath, contentSourceData.defaultLocaleCode);
|
|
1158
1171
|
} else {
|
|
1159
1172
|
assets = _.reduce(
|
|
1160
1173
|
this.contentSourceDataById,
|
|
1161
1174
|
(result: ContentStoreTypes.APIAsset[], contentSourceData) => {
|
|
1162
|
-
const assets = mapStoreAssetsToAPIAssets(contentSourceData.assets, contentSourceData.defaultLocaleCode);
|
|
1175
|
+
const assets = mapStoreAssetsToAPIAssets(contentSourceData.assets, this.staticAssetsPublicPath, contentSourceData.defaultLocaleCode);
|
|
1163
1176
|
return result.concat(assets);
|
|
1164
1177
|
},
|
|
1165
1178
|
[]
|
|
@@ -1283,10 +1296,10 @@ export class ContentStore {
|
|
|
1283
1296
|
refId: result.srcDocumentId
|
|
1284
1297
|
} as CSITypes.UpdateOperationReferenceField;
|
|
1285
1298
|
} else {
|
|
1286
|
-
if (!['string', 'text', 'json']
|
|
1299
|
+
if (!isOneOfFieldTypes(csiFieldProps.type, ['string', 'text', 'json', 'cross-reference'])) {
|
|
1287
1300
|
throw new Error(`The 'cross-reference' field can be only applied on string, text and json fields: ${fieldPath.join('.')}`);
|
|
1288
1301
|
}
|
|
1289
|
-
field = updateOperationValueFieldWithCrossReference(csiFieldProps.type
|
|
1302
|
+
field = updateOperationValueFieldWithCrossReference(csiFieldProps.type, {
|
|
1290
1303
|
refId: result.srcDocumentId,
|
|
1291
1304
|
refSrcType: refSrcType,
|
|
1292
1305
|
refProjectId: refProjectId
|
|
@@ -1733,7 +1746,7 @@ export class ContentStore {
|
|
|
1733
1746
|
contentSourceInstance: contentSourceData.instance,
|
|
1734
1747
|
defaultLocaleCode: contentSourceData.defaultLocaleCode
|
|
1735
1748
|
});
|
|
1736
|
-
return mapStoreAssetsToAPIAssets(storeAssets, locale);
|
|
1749
|
+
return mapStoreAssetsToAPIAssets(storeAssets, this.staticAssetsPublicPath, locale);
|
|
1737
1750
|
}
|
|
1738
1751
|
|
|
1739
1752
|
async deleteDocument({
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,8 @@ export * as consts from './consts';
|
|
|
6
6
|
export * from './common/common-schema';
|
|
7
7
|
export * from './common/common-types';
|
|
8
8
|
export * from './content-store';
|
|
9
|
-
export * as ContentStoreTypes from './
|
|
9
|
+
export * as ContentStoreTypes from './types';
|
|
10
10
|
export { default as encodeData } from './encoder';
|
|
11
11
|
export * from './encoder';
|
|
12
12
|
export * from './types/search-filter';
|
|
13
|
+
export * from './services';
|
|
@@ -0,0 +1,245 @@
|
|
|
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, GitFileCommitDescriptor, GitAuthor, GitCommitLogEntry, Logger } from '@stackbit/types';
|
|
8
|
+
import { Worker } from '@stackbit/utils';
|
|
9
|
+
|
|
10
|
+
import { DocumentStatus } from '@stackbit/types';
|
|
11
|
+
import { CommandRunner } 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 runCommand: CommandRunner;
|
|
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
|
+
runCommand: CommandRunner;
|
|
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.runCommand = options.runCommand;
|
|
47
|
+
this.logger = options.logger;
|
|
48
|
+
this.userLogger = options.userLogger;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getRepoUrl() {
|
|
52
|
+
return this.repoUrl;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getRepoBranch() {
|
|
56
|
+
return this.repoBranch;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getRepoPublishBranch() {
|
|
60
|
+
return this.repoPublishBranch;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async commit(author: GitAuthor, files: GitFileCommitDescriptor[]) {
|
|
64
|
+
const filePaths = _.map(files, 'filePath');
|
|
65
|
+
this.logger.debug('[git] Commit scheduled', filePaths);
|
|
66
|
+
return this.worker.schedule(async () => {
|
|
67
|
+
this.logger.debug('[git] Commit running', filePaths);
|
|
68
|
+
const message = files
|
|
69
|
+
.reduce((messages: string[], file) => {
|
|
70
|
+
messages.push(`${path.parse(file.filePath).base}: ${file.description}`);
|
|
71
|
+
return messages;
|
|
72
|
+
}, [])
|
|
73
|
+
.join('.\n');
|
|
74
|
+
await this.runCommand('git', ['add', ...filePaths], { cwd: this.repoDir });
|
|
75
|
+
await this.runCommand('git', ['commit', '--no-verify', '--author', `${author.name || author.email} <${author.email}>`, '-m', message], {
|
|
76
|
+
cwd: this.repoDir
|
|
77
|
+
});
|
|
78
|
+
this.logger.debug('[git] Commit done', filePaths);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private push() {
|
|
83
|
+
this.logger.debug('[git] Push scheduled');
|
|
84
|
+
return this.worker.schedule(async () => {
|
|
85
|
+
this.logger.debug('[git] Push running');
|
|
86
|
+
await this.runCommand('rm', ['-rf', '.git/rebase-merge'], { cwd: this.repoDir }).catch((err) => {}); // fixes leftover rebase directory with autostash
|
|
87
|
+
await this.runCommand('git', ['pull', 'origin', this.repoBranch, '--rebase', '--autostash', '-Xtheirs'], { cwd: this.repoDir });
|
|
88
|
+
await this.runCommand('git', ['push', 'origin', this.repoBranch], { cwd: this.repoDir });
|
|
89
|
+
this.logger.debug('[git] Push done');
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async commitAndPush(author: GitAuthor, files: GitFileCommitDescriptor[]): Promise<void> {
|
|
94
|
+
await this.commit(author, files);
|
|
95
|
+
return this.push();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
pull(): Promise<void> {
|
|
99
|
+
this.logger.debug('[git] Pull scheduled');
|
|
100
|
+
return this.worker.schedule(async () => {
|
|
101
|
+
this.logger.debug('[git] Pull running');
|
|
102
|
+
await this.runCommand('git', ['pull', 'origin', '--rebase', '--autostash', '-Xtheirs'], { cwd: this.repoDir });
|
|
103
|
+
this.logger.debug('[git] Pull done');
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async publishAll(author: GitAuthor) {
|
|
108
|
+
this.logger.debug('[git] Publish all started');
|
|
109
|
+
const publishDir = path.join(os.tmpdir(), uuid());
|
|
110
|
+
await this.runCommand('git', ['clone', this.repoUrl, '--branch', this.repoPublishBranch, publishDir]);
|
|
111
|
+
try {
|
|
112
|
+
await this.runCommand('git', ['merge', `origin/${this.repoBranch}`, this.repoPublishBranch, '-Xtheirs'], { cwd: publishDir });
|
|
113
|
+
await this.runCommand('git', ['commit', '--author', `${author.name || author.email} <${author.email}>`, `-m`, 'Publish'], {
|
|
114
|
+
cwd: publishDir
|
|
115
|
+
}).catch(() => {});
|
|
116
|
+
await this.runCommand('git', ['push', 'origin'], { cwd: publishDir });
|
|
117
|
+
} finally {
|
|
118
|
+
await fse.remove(publishDir);
|
|
119
|
+
}
|
|
120
|
+
this.logger.debug('[git] Publish all done');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private async publishFiles(author: GitAuthor, filePaths: string[]) {
|
|
124
|
+
this.logger.debug('[git] Publish files started', filePaths);
|
|
125
|
+
const publishDir = path.join(os.tmpdir(), uuid());
|
|
126
|
+
await this.runCommand('git', ['clone', this.repoUrl, '--branch', this.repoPublishBranch, publishDir]);
|
|
127
|
+
try {
|
|
128
|
+
await Promise.all(
|
|
129
|
+
filePaths.map(async (filePath) => {
|
|
130
|
+
const destFilePath = path.join(publishDir, filePath);
|
|
131
|
+
await fse.ensureDir(path.dirname(destFilePath));
|
|
132
|
+
return fse.copy(path.join(this.repoDir, filePath), destFilePath);
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
await this.runCommand('git', ['checkout', '-b', 'stackbit-publish-branch'], { cwd: publishDir });
|
|
137
|
+
await this.runCommand('git', ['add', ...filePaths], { cwd: publishDir });
|
|
138
|
+
await this.runCommand('git', ['commit', '--author', `${author.name || author.email} <${author.email}>`, `-m`, 'Publish'], {
|
|
139
|
+
cwd: publishDir
|
|
140
|
+
}).catch((err) => {
|
|
141
|
+
return;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await this.runCommand('git', ['checkout', this.repoBranch], { cwd: publishDir });
|
|
145
|
+
await this.runCommand('git', ['merge', 'stackbit-publish-branch', this.repoBranch, '-Xtheirs'], { cwd: publishDir });
|
|
146
|
+
|
|
147
|
+
await this.runCommand('git', ['checkout', this.repoPublishBranch], { cwd: publishDir });
|
|
148
|
+
await this.runCommand('git', ['merge', 'stackbit-publish-branch', this.repoPublishBranch, '-Xtheirs'], { cwd: publishDir });
|
|
149
|
+
|
|
150
|
+
await this.runCommand('git', ['push', 'origin', this.repoPublishBranch, this.repoBranch], { cwd: publishDir });
|
|
151
|
+
} finally {
|
|
152
|
+
await fse.remove(publishDir);
|
|
153
|
+
}
|
|
154
|
+
this.logger.debug('[git] Publish files done', filePaths);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
publish(author: GitAuthor, filePaths?: string[]): Promise<void> {
|
|
158
|
+
this.logger.debug('[git] Publish scheduled');
|
|
159
|
+
return this.worker.schedule(async () => {
|
|
160
|
+
if (filePaths) {
|
|
161
|
+
if (!filePaths.length) {
|
|
162
|
+
this.logger.debug('[git] Nothing to publish');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
return this.publishFiles(author, filePaths);
|
|
166
|
+
} else {
|
|
167
|
+
return this.publishAll(author);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private parseGitCommitAuthor(author?: string) {
|
|
173
|
+
if (!author) {
|
|
174
|
+
return author;
|
|
175
|
+
}
|
|
176
|
+
const regex = /(.*)\((.*)\)/;
|
|
177
|
+
const match = author.match(regex);
|
|
178
|
+
if (match) {
|
|
179
|
+
const [authorEmail, authorName] = match.slice(1);
|
|
180
|
+
|
|
181
|
+
if (authorName === 'Stackbit Code Editor') {
|
|
182
|
+
return 'stackbit';
|
|
183
|
+
}
|
|
184
|
+
return authorEmail ? authorEmail.toLowerCase() : author;
|
|
185
|
+
}
|
|
186
|
+
return author;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async diff(): Promise<string[]> {
|
|
190
|
+
this.logger.debug('[git] Diff check scheduled');
|
|
191
|
+
return this.worker.schedule(async () => {
|
|
192
|
+
this.logger.debug('[git] Diff check running');
|
|
193
|
+
const result = await this.runCommand(
|
|
194
|
+
'git',
|
|
195
|
+
[
|
|
196
|
+
'diff',
|
|
197
|
+
'--name-only',
|
|
198
|
+
'--no-renames', // this flag makes sure we get both old and new name of renamed file
|
|
199
|
+
`origin/${this.repoPublishBranch}..${this.repoBranch}`
|
|
200
|
+
],
|
|
201
|
+
{ cwd: this.repoDir }
|
|
202
|
+
);
|
|
203
|
+
this.logger.debug('[git] Diff check done');
|
|
204
|
+
return result.stdout.split('\n').filter(Boolean);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async commitLog(): Promise<GitCommitLogEntry[]> {
|
|
209
|
+
this.logger.debug('[git] Changes check scheduled');
|
|
210
|
+
return this.worker.schedule(async () => {
|
|
211
|
+
this.logger.debug('[git] Changes check running');
|
|
212
|
+
if (!this.branchFetched) {
|
|
213
|
+
await this.runCommand('git', ['fetch', 'origin', `${this.repoPublishBranch}:${this.repoPublishBranch}`], { cwd: this.repoDir });
|
|
214
|
+
this.branchFetched = true;
|
|
215
|
+
}
|
|
216
|
+
const logResult = await this.runCommand(
|
|
217
|
+
'git',
|
|
218
|
+
['log', '--pretty=format:commit:%H%n%at%n%ae%x28%an%x29', '--name-status', `${this.repoPublishBranch}..${this.repoBranch}`],
|
|
219
|
+
{ cwd: this.repoDir }
|
|
220
|
+
);
|
|
221
|
+
this.logger.debug('[git] Changes check done');
|
|
222
|
+
return logResult.stdout
|
|
223
|
+
.split('commit:')
|
|
224
|
+
.filter(Boolean)
|
|
225
|
+
.map((rawCommit) => {
|
|
226
|
+
const split = rawCommit.trim().split('\n');
|
|
227
|
+
return {
|
|
228
|
+
author: this.parseGitCommitAuthor(split[2]),
|
|
229
|
+
timestamp: split[1] ? new Date(parseInt(split[1]) * 1000) : undefined,
|
|
230
|
+
commitHash: split[0],
|
|
231
|
+
changes: split
|
|
232
|
+
.slice(3)
|
|
233
|
+
.map((line) => line.trim().split(/\t/))
|
|
234
|
+
.filter(Boolean)
|
|
235
|
+
.filter(([status, filename]) => status && filename)
|
|
236
|
+
.map(([status, filename]) => ({
|
|
237
|
+
status: GIT_LOG_CHANGE_TYPES[status!] || 'modified',
|
|
238
|
+
filePath: filename
|
|
239
|
+
}))
|
|
240
|
+
};
|
|
241
|
+
})
|
|
242
|
+
.reverse();
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import childProcess, { ChildProcessWithoutNullStreams } from 'child_process';
|
|
2
|
+
import { CommandRunner, RunResult } from '@stackbit/types';
|
|
3
|
+
|
|
4
|
+
export function getCommandRunner(commandRunnerOptions: { env: NodeJS.ProcessEnv; uid?: number }): CommandRunner {
|
|
5
|
+
return (command: string, args?: string[], options?: { cwd?: string; shell?: boolean; env?: NodeJS.ProcessEnv }): Promise<RunResult> => {
|
|
6
|
+
return getProcessPromise(
|
|
7
|
+
childProcess.spawn(command, args, {
|
|
8
|
+
cwd: options?.cwd,
|
|
9
|
+
shell: options?.shell,
|
|
10
|
+
env: {
|
|
11
|
+
...commandRunnerOptions.env,
|
|
12
|
+
...options?.env
|
|
13
|
+
},
|
|
14
|
+
...(commandRunnerOptions.uid ? { uid: commandRunnerOptions.uid } : {})
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getProcessPromise(p: ChildProcessWithoutNullStreams): Promise<{
|
|
21
|
+
stdout: string;
|
|
22
|
+
stderr: string;
|
|
23
|
+
exitCode?: number;
|
|
24
|
+
err?: Error;
|
|
25
|
+
}> {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
let stdout = '';
|
|
28
|
+
let stderr = '';
|
|
29
|
+
p.stdout.on('data', (out) => (stdout += out));
|
|
30
|
+
p.stderr.on('data', (out) => (stderr += out));
|
|
31
|
+
p.on('exit', (exitCode) => {
|
|
32
|
+
if (exitCode !== 0) {
|
|
33
|
+
reject({
|
|
34
|
+
err: new Error(`process exited with code: ${exitCode}, stderr: ${stderr}`),
|
|
35
|
+
stdout,
|
|
36
|
+
stderr,
|
|
37
|
+
exitCode
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
resolve({
|
|
41
|
+
stdout,
|
|
42
|
+
stderr
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
p.on('error', (err) => {
|
|
47
|
+
reject({
|
|
48
|
+
err,
|
|
49
|
+
stdout,
|
|
50
|
+
stderr
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|