@stackbit/cms-core 0.1.25 → 0.1.26-develop.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.
Files changed (103) hide show
  1. package/dist/content-store-utils.d.ts +5 -5
  2. package/dist/content-store-utils.d.ts.map +1 -1
  3. package/dist/content-store-utils.js +15 -3
  4. package/dist/content-store-utils.js.map +1 -1
  5. package/dist/content-store.d.ts +11 -3
  6. package/dist/content-store.d.ts.map +1 -1
  7. package/dist/content-store.js +10 -5
  8. package/dist/content-store.js.map +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/services/git.d.ts +38 -0
  14. package/dist/services/git.d.ts.map +1 -0
  15. package/dist/services/git.js +201 -0
  16. package/dist/services/git.js.map +1 -0
  17. package/dist/services/index.d.ts +3 -0
  18. package/dist/services/index.d.ts.map +1 -0
  19. package/dist/services/index.js +15 -0
  20. package/dist/services/index.js.map +1 -0
  21. package/dist/services/run.d.ts +7 -0
  22. package/dist/services/run.d.ts.map +1 -0
  23. package/dist/services/run.js +53 -0
  24. package/dist/services/run.js.map +1 -0
  25. package/dist/types/content-store-document-fields.d.ts +600 -0
  26. package/dist/types/content-store-document-fields.d.ts.map +1 -0
  27. package/dist/types/content-store-document-fields.js +3 -0
  28. package/dist/types/content-store-document-fields.js.map +1 -0
  29. package/dist/types/content-store-documents.d.ts +99 -0
  30. package/dist/types/content-store-documents.d.ts.map +1 -0
  31. package/dist/types/content-store-documents.js +3 -0
  32. package/dist/types/content-store-documents.js.map +1 -0
  33. package/dist/types/content-store-types.d.ts +75 -0
  34. package/dist/types/content-store-types.d.ts.map +1 -0
  35. package/dist/types/content-store-types.js.map +1 -0
  36. package/dist/types/content-store-update-operation.d.ts +61 -0
  37. package/dist/types/content-store-update-operation.d.ts.map +1 -0
  38. package/dist/types/content-store-update-operation.js +3 -0
  39. package/dist/types/content-store-update-operation.js.map +1 -0
  40. package/dist/types/index.d.ts +6 -0
  41. package/dist/types/index.d.ts.map +1 -0
  42. package/dist/types/index.js +18 -0
  43. package/dist/types/index.js.map +1 -0
  44. package/dist/types/search-filter.d.ts +1 -1
  45. package/dist/types/search-filter.d.ts.map +1 -1
  46. package/dist/utils/create-update-csi-docs.d.ts +1 -1
  47. package/dist/utils/create-update-csi-docs.d.ts.map +1 -1
  48. package/dist/utils/create-update-csi-docs.js +28 -14
  49. package/dist/utils/create-update-csi-docs.js.map +1 -1
  50. package/dist/utils/csi-to-store-docs-converter.d.ts +1 -1
  51. package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -1
  52. package/dist/utils/csi-to-store-docs-converter.js +20 -4
  53. package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
  54. package/dist/utils/duplicate-document.d.ts +1 -1
  55. package/dist/utils/duplicate-document.d.ts.map +1 -1
  56. package/dist/utils/duplicate-document.js +11 -0
  57. package/dist/utils/duplicate-document.js.map +1 -1
  58. package/dist/utils/index.d.ts +2 -2
  59. package/dist/utils/index.d.ts.map +1 -1
  60. package/dist/utils/search-utils.d.ts +1 -1
  61. package/dist/utils/search-utils.d.ts.map +1 -1
  62. package/dist/utils/search-utils.js +16 -16
  63. package/dist/utils/search-utils.js.map +1 -1
  64. package/dist/utils/site-map.d.ts +1 -1
  65. package/dist/utils/site-map.d.ts.map +1 -1
  66. package/dist/utils/site-map.js +9 -0
  67. package/dist/utils/site-map.js.map +1 -1
  68. package/dist/utils/store-to-api-docs-converter.d.ts +2 -2
  69. package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
  70. package/dist/utils/store-to-api-docs-converter.js +87 -38
  71. package/dist/utils/store-to-api-docs-converter.js.map +1 -1
  72. package/dist/utils/store-to-csi-docs-converter.d.ts +1 -1
  73. package/dist/utils/store-to-csi-docs-converter.d.ts.map +1 -1
  74. package/dist/utils/store-to-csi-docs-converter.js +4 -0
  75. package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
  76. package/dist/utils/timer.d.ts +1 -1
  77. package/dist/utils/timer.d.ts.map +1 -1
  78. package/package.json +9 -6
  79. package/src/content-store-utils.ts +19 -8
  80. package/src/content-store.ts +28 -15
  81. package/src/index.ts +2 -1
  82. package/src/services/git.ts +245 -0
  83. package/src/services/index.ts +2 -0
  84. package/src/services/run.ts +54 -0
  85. package/src/types/content-store-document-fields.ts +658 -0
  86. package/src/types/content-store-documents.ts +113 -0
  87. package/src/types/content-store-types.ts +96 -0
  88. package/src/types/content-store-update-operation.ts +85 -0
  89. package/src/types/index.ts +5 -0
  90. package/src/types/search-filter.ts +26 -19
  91. package/src/utils/create-update-csi-docs.ts +33 -16
  92. package/src/utils/csi-to-store-docs-converter.ts +33 -14
  93. package/src/utils/duplicate-document.ts +11 -1
  94. package/src/utils/search-utils.ts +18 -19
  95. package/src/utils/site-map.ts +10 -1
  96. package/src/utils/store-to-api-docs-converter.ts +86 -38
  97. package/src/utils/store-to-csi-docs-converter.ts +5 -1
  98. package/src/utils/timer.ts +1 -1
  99. package/dist/content-store-types.d.ts +0 -411
  100. package/dist/content-store-types.d.ts.map +0 -1
  101. package/dist/content-store-types.js.map +0 -1
  102. package/src/content-store-types.ts +0 -527
  103. /package/dist/{content-store-types.js → types/content-store-types.js} +0 -0
@@ -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,2 @@
1
+ export * from './run';
2
+ export * from './git';
@@ -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
+ }