@slicemachine/init 1.1.9-alpha.3 → 1.1.10-alpha.2
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/build/index.js +399 -11
- package/build/index.js.map +1 -1
- package/jest.config.js +1 -0
- package/package.json +13 -3
- package/src/index.ts +11 -3
- package/src/steps/configure-project.ts +14 -3
- package/src/steps/display-final-message.ts +2 -1
- package/src/steps/index.ts +1 -0
- package/src/steps/install-required-dependencies.ts +29 -10
- package/src/steps/sendStarterData.ts +35 -0
- package/src/steps/starters/communication.ts +171 -0
- package/src/steps/starters/custom-types.ts +82 -0
- package/src/steps/starters/documents.ts +114 -0
- package/src/steps/starters/endpoints.ts +20 -0
- package/src/steps/starters/prompts.ts +29 -0
- package/src/steps/starters/s3.ts +201 -0
- package/src/steps/starters/slices.ts +62 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { CustomType } from "@prismicio/types-internal/lib/customtypes";
|
|
2
|
+
import { Slices, SliceSM } from "@slicemachine/core/build/models";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import { logs } from "../../utils";
|
|
5
|
+
import type { ApiEndpoints } from "./endpoints";
|
|
6
|
+
|
|
7
|
+
export function handleErrors(prefix: string, err: unknown) {
|
|
8
|
+
if (axios.isAxiosError(err) && err.response) {
|
|
9
|
+
logs.writeError(
|
|
10
|
+
`${prefix} | [${err.response.status}]: ${err.response.statusText}`
|
|
11
|
+
);
|
|
12
|
+
} else if (err instanceof Error) {
|
|
13
|
+
logs.writeError(`${prefix} ${err.message}`);
|
|
14
|
+
} else {
|
|
15
|
+
logs.writeError(`${prefix} ${String(err)}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function getRemoteSliceIds(
|
|
20
|
+
customTypeApiEndpoint: ApiEndpoints["Models"],
|
|
21
|
+
repository: string,
|
|
22
|
+
authorization: string
|
|
23
|
+
): Promise<Array<string>> {
|
|
24
|
+
const addr = `${stripLastSlash(customTypeApiEndpoint)}/slices`;
|
|
25
|
+
return axios
|
|
26
|
+
.get<Array<{ id: string }>>(addr, {
|
|
27
|
+
headers: {
|
|
28
|
+
Authorization: `Bearer ${authorization}`,
|
|
29
|
+
repository,
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
.then((res) => {
|
|
33
|
+
return Array.isArray(res.data) ? res.data.map((model) => model.id) : [];
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function sendModelToPrismic(
|
|
38
|
+
repository: string,
|
|
39
|
+
authorization: string,
|
|
40
|
+
customTypesApiEndpoint: string,
|
|
41
|
+
remoteSliceIds: Array<string>,
|
|
42
|
+
model: SliceSM
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
const data = Slices.fromSM(model);
|
|
45
|
+
const updateOrInsertUrl = `${customTypesApiEndpoint}slices/${
|
|
46
|
+
remoteSliceIds.includes(model.id) ? "update" : "insert"
|
|
47
|
+
}`;
|
|
48
|
+
|
|
49
|
+
return axios
|
|
50
|
+
.post(updateOrInsertUrl, data, {
|
|
51
|
+
headers: {
|
|
52
|
+
Authorization: `Bearer ${authorization}`,
|
|
53
|
+
repository,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
.then(() => {
|
|
57
|
+
return;
|
|
58
|
+
})
|
|
59
|
+
.catch((err) => {
|
|
60
|
+
handleErrors(
|
|
61
|
+
`sending slice ${model.id}, please try again. If the problem persists, contact us.`,
|
|
62
|
+
err
|
|
63
|
+
);
|
|
64
|
+
throw err;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function sendManyModelsToPrismic(
|
|
69
|
+
repository: string,
|
|
70
|
+
authorization: string,
|
|
71
|
+
customTypesApiEndpoint: string,
|
|
72
|
+
remoteSliceIds: Array<string>,
|
|
73
|
+
models: Array<SliceSM>
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
return Promise.all(
|
|
76
|
+
models.map((model) =>
|
|
77
|
+
sendModelToPrismic(
|
|
78
|
+
repository,
|
|
79
|
+
authorization,
|
|
80
|
+
customTypesApiEndpoint,
|
|
81
|
+
remoteSliceIds,
|
|
82
|
+
model
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
.then(() => {
|
|
87
|
+
return;
|
|
88
|
+
})
|
|
89
|
+
.catch(() => {
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function stripLastSlash(str: string) {
|
|
95
|
+
return str.replace(/\/*$/g, "");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function getRemoteCustomTypeIds(
|
|
99
|
+
customTypeApiEndpoint: ApiEndpoints["Models"],
|
|
100
|
+
repository: string,
|
|
101
|
+
authorization: string
|
|
102
|
+
): Promise<Array<string>> {
|
|
103
|
+
const addr = `${stripLastSlash(customTypeApiEndpoint)}/customtypes`;
|
|
104
|
+
return axios
|
|
105
|
+
.get<CustomType[]>(addr, {
|
|
106
|
+
headers: {
|
|
107
|
+
Authorization: `Bearer ${authorization}`,
|
|
108
|
+
repository,
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
.then((res) => {
|
|
112
|
+
return Array.isArray(res.data) ? res.data.map((ct) => ct.id) : [];
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function sendCustomTypeToPrismic(
|
|
117
|
+
repository: string,
|
|
118
|
+
authorization: string,
|
|
119
|
+
customTypeApiEndpoint: string,
|
|
120
|
+
remoteCustomTypeIds: Array<string>,
|
|
121
|
+
customType: CustomType
|
|
122
|
+
): Promise<void> {
|
|
123
|
+
const shouldUpdate = remoteCustomTypeIds.includes(customType.id);
|
|
124
|
+
const addr = `${stripLastSlash(customTypeApiEndpoint)}/customtypes/${
|
|
125
|
+
shouldUpdate ? "update" : "insert"
|
|
126
|
+
}`;
|
|
127
|
+
|
|
128
|
+
return axios
|
|
129
|
+
.post(addr, customType, {
|
|
130
|
+
headers: {
|
|
131
|
+
repository,
|
|
132
|
+
Authorization: `Bearer ${authorization}`,
|
|
133
|
+
},
|
|
134
|
+
})
|
|
135
|
+
.then(() => {
|
|
136
|
+
return;
|
|
137
|
+
})
|
|
138
|
+
.catch((err) => {
|
|
139
|
+
handleErrors(
|
|
140
|
+
`sending custom type ${customType.id}, please try again. If the problem persists, contact us.`,
|
|
141
|
+
err
|
|
142
|
+
);
|
|
143
|
+
throw err;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function sendManyCustomTypesToPrismic(
|
|
148
|
+
repository: string,
|
|
149
|
+
authorization: string,
|
|
150
|
+
customTypeApiEndpoint: string,
|
|
151
|
+
remoteCustomTypeIds: Array<string>,
|
|
152
|
+
customTypes: Array<CustomType>
|
|
153
|
+
): Promise<void> {
|
|
154
|
+
return Promise.all(
|
|
155
|
+
customTypes.map((customType) =>
|
|
156
|
+
sendCustomTypeToPrismic(
|
|
157
|
+
repository,
|
|
158
|
+
authorization,
|
|
159
|
+
customTypeApiEndpoint,
|
|
160
|
+
remoteCustomTypeIds,
|
|
161
|
+
customType
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
.then(() => {
|
|
166
|
+
return;
|
|
167
|
+
})
|
|
168
|
+
.catch(() => {
|
|
169
|
+
process.exit(1);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { CustomType } from "@prismicio/types-internal/lib/customtypes";
|
|
2
|
+
import { Files, CustomTypesPaths } from "@slicemachine/core/build/node-utils";
|
|
3
|
+
import { isLeft } from "fp-ts/lib/Either";
|
|
4
|
+
import {
|
|
5
|
+
getRemoteCustomTypeIds,
|
|
6
|
+
sendManyCustomTypesToPrismic,
|
|
7
|
+
} from "./communication";
|
|
8
|
+
import { promptToPushCustomTypes } from "./prompts";
|
|
9
|
+
import { getEndpointsFromBase } from "./endpoints";
|
|
10
|
+
import { logs } from "../../utils";
|
|
11
|
+
|
|
12
|
+
export function readCustomTypes(cwd: string): Array<CustomType> {
|
|
13
|
+
const customTypePaths = CustomTypesPaths(cwd);
|
|
14
|
+
const dir = customTypePaths.value();
|
|
15
|
+
|
|
16
|
+
if (Files.isDirectory(dir) === false) return [];
|
|
17
|
+
|
|
18
|
+
const fileNames = Files.readDirectory(dir);
|
|
19
|
+
|
|
20
|
+
const files = fileNames.reduce<Array<CustomType>>((acc, fileName) => {
|
|
21
|
+
const filePath = customTypePaths.customType(fileName).model();
|
|
22
|
+
const json = Files.safeReadJson(filePath);
|
|
23
|
+
|
|
24
|
+
if (!json) return acc;
|
|
25
|
+
|
|
26
|
+
const file = CustomType.decode(json);
|
|
27
|
+
|
|
28
|
+
if (file instanceof Error) {
|
|
29
|
+
logs.writeError(`reading ${filePath}: ${file.message}`);
|
|
30
|
+
return acc;
|
|
31
|
+
}
|
|
32
|
+
if (isLeft(file)) {
|
|
33
|
+
logs.writeError(`validating ${filePath}: ${JSON.stringify(file.left)}`);
|
|
34
|
+
return acc;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return [...acc, file.right];
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
return files;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function sendCustomTypesFromStarter(
|
|
44
|
+
repository: string,
|
|
45
|
+
authorization: string,
|
|
46
|
+
base: string,
|
|
47
|
+
cwd: string
|
|
48
|
+
) {
|
|
49
|
+
const customTypeApiEndpoint = getEndpointsFromBase(base).Models;
|
|
50
|
+
|
|
51
|
+
const customTypes = readCustomTypes(cwd);
|
|
52
|
+
|
|
53
|
+
if (customTypes.length === 0) return Promise.resolve(false);
|
|
54
|
+
|
|
55
|
+
const remoteCustomTypeIds = await getRemoteCustomTypeIds(
|
|
56
|
+
customTypeApiEndpoint,
|
|
57
|
+
repository,
|
|
58
|
+
authorization
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (remoteCustomTypeIds.length) {
|
|
62
|
+
const shouldPush = await promptToPushCustomTypes();
|
|
63
|
+
if (shouldPush === false) return Promise.resolve(false);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const spinner = logs.spinner(
|
|
67
|
+
"Pushing existing custom types to your repository"
|
|
68
|
+
);
|
|
69
|
+
spinner.start();
|
|
70
|
+
|
|
71
|
+
await sendManyCustomTypesToPrismic(
|
|
72
|
+
repository,
|
|
73
|
+
authorization,
|
|
74
|
+
customTypeApiEndpoint,
|
|
75
|
+
remoteCustomTypeIds,
|
|
76
|
+
customTypes
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
spinner.succeed();
|
|
80
|
+
|
|
81
|
+
return Promise.resolve(true);
|
|
82
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import axios, { AxiosError } from "axios";
|
|
4
|
+
|
|
5
|
+
import * as t from "io-ts";
|
|
6
|
+
import { getOrElseW } from "fp-ts/Either";
|
|
7
|
+
import { handleErrors } from "./communication";
|
|
8
|
+
import { logs } from "../../utils";
|
|
9
|
+
|
|
10
|
+
const SignatureFileReader = t.type({
|
|
11
|
+
signature: t.string,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
type SignatureFile = t.TypeOf<typeof SignatureFileReader>;
|
|
15
|
+
|
|
16
|
+
export async function readSignatureFile(cwd: string): Promise<SignatureFile> {
|
|
17
|
+
const pathToFile = path.join(cwd, "documents", "index.json");
|
|
18
|
+
return fs.promises.readFile(pathToFile, "utf-8").then((res) => {
|
|
19
|
+
const data = JSON.parse(res) as unknown;
|
|
20
|
+
return getOrElseW(() => {
|
|
21
|
+
throw new Error("Unable to read document signature file");
|
|
22
|
+
})(SignatureFileReader.decode(data));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function lsdir(dir: string): Promise<Array<string>> {
|
|
27
|
+
return fs.promises.readdir(dir).then((dirs) => {
|
|
28
|
+
return dirs
|
|
29
|
+
.filter((name) => fs.statSync(path.join(dir, name)).isDirectory())
|
|
30
|
+
.map((subdirectory) => path.join(dir, subdirectory));
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function lsfiles(dir: string): Promise<Array<string>> {
|
|
35
|
+
return fs.promises.readdir(dir).then((dirs) => {
|
|
36
|
+
return dirs
|
|
37
|
+
.filter((name) => fs.statSync(path.join(dir, name)).isFile())
|
|
38
|
+
.map((file) => path.join(dir, file));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function readDocuments(cwd: string) {
|
|
43
|
+
const documentDir = path.join(cwd, "documents");
|
|
44
|
+
const dirs = await lsdir(documentDir);
|
|
45
|
+
|
|
46
|
+
const files = (await Promise.all(dirs.map((dir) => lsfiles(dir)))).flat();
|
|
47
|
+
|
|
48
|
+
const documentObj = files.reduce<Record<string, unknown>>((acc, file) => {
|
|
49
|
+
const fileContent = fs.readFileSync(file, "utf-8");
|
|
50
|
+
const filename = path.parse(file).name;
|
|
51
|
+
acc[filename] = JSON.parse(fileContent);
|
|
52
|
+
return acc;
|
|
53
|
+
}, {});
|
|
54
|
+
|
|
55
|
+
return JSON.stringify(documentObj);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const sendDocumentsFromStarter = async (
|
|
59
|
+
repository: string,
|
|
60
|
+
cookies: string,
|
|
61
|
+
base: string,
|
|
62
|
+
cwd: string
|
|
63
|
+
): Promise<boolean> => {
|
|
64
|
+
const pathToDocuments = path.join(cwd, "documents");
|
|
65
|
+
const pathToSignatureFile = path.join(pathToDocuments, "index.json");
|
|
66
|
+
|
|
67
|
+
if (!fs.existsSync(pathToSignatureFile)) {
|
|
68
|
+
return Promise.resolve(false);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const signatureObj = await readSignatureFile(cwd);
|
|
72
|
+
const documentsStr = await readDocuments(cwd);
|
|
73
|
+
|
|
74
|
+
const payload = {
|
|
75
|
+
signature: signatureObj.signature,
|
|
76
|
+
documents: documentsStr,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const prismicUrl = new URL(base);
|
|
80
|
+
prismicUrl.hostname = `${repository}.${prismicUrl.hostname}`;
|
|
81
|
+
prismicUrl.pathname = "starter/documents";
|
|
82
|
+
const endpointURL = prismicUrl.toString();
|
|
83
|
+
|
|
84
|
+
const spinner = logs.spinner("Pushing existing documents to your repository");
|
|
85
|
+
spinner.start();
|
|
86
|
+
|
|
87
|
+
return axios
|
|
88
|
+
.post(endpointURL, payload, {
|
|
89
|
+
headers: {
|
|
90
|
+
"User-Agent": "prismic-cli/0",
|
|
91
|
+
Cookie: cookies,
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
.then(() => {
|
|
95
|
+
spinner.succeed();
|
|
96
|
+
fs.rmSync(pathToDocuments, { recursive: true, force: true });
|
|
97
|
+
return true;
|
|
98
|
+
})
|
|
99
|
+
.catch((e: AxiosError) => {
|
|
100
|
+
spinner.fail();
|
|
101
|
+
if (e.response?.data === "Repository should not contain documents") {
|
|
102
|
+
logs.writeError(
|
|
103
|
+
"The selected repository is not empty, documents cannot be uploaded. Please choose an empty repository or delete the documents contained in your repository."
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
handleErrors(
|
|
107
|
+
"sending documents, please try again. If the problem persists, contact us.",
|
|
108
|
+
e
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
process.exit(1);
|
|
113
|
+
});
|
|
114
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ApiEndpoints = {
|
|
2
|
+
Models: string;
|
|
3
|
+
AclProvider: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const ProductionApisEndpoints: ApiEndpoints = {
|
|
7
|
+
Models: "https://customtypes.prismic.io/",
|
|
8
|
+
AclProvider: "https://0yyeb2g040.execute-api.us-east-1.amazonaws.com/prod/",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const StageApisEndpoints: ApiEndpoints = {
|
|
12
|
+
Models: "https://customtypes.wroom.io/",
|
|
13
|
+
AclProvider: "https://2iamcvnxf4.execute-api.us-east-1.amazonaws.com/stage/",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getEndpointsFromBase = (base: string): ApiEndpoints => {
|
|
17
|
+
const url = new URL(base);
|
|
18
|
+
if (url.hostname === "wroom.io") return StageApisEndpoints;
|
|
19
|
+
return ProductionApisEndpoints;
|
|
20
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
|
|
3
|
+
export async function promptToPushSlices(): Promise<boolean> {
|
|
4
|
+
return inquirer
|
|
5
|
+
.prompt<{ pushSlices: boolean }>([
|
|
6
|
+
{
|
|
7
|
+
type: "confirm",
|
|
8
|
+
name: "pushSlices",
|
|
9
|
+
default: false,
|
|
10
|
+
message:
|
|
11
|
+
"Your repository already contains Slices. Do you want to continue pushing your local Slices?",
|
|
12
|
+
},
|
|
13
|
+
])
|
|
14
|
+
.then((res) => res.pushSlices);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function promptToPushCustomTypes(): Promise<boolean> {
|
|
18
|
+
return inquirer
|
|
19
|
+
.prompt<{ pushCustomTypes: boolean }>([
|
|
20
|
+
{
|
|
21
|
+
type: "confirm",
|
|
22
|
+
name: "pushCustomTypes",
|
|
23
|
+
default: false,
|
|
24
|
+
message:
|
|
25
|
+
"Your repository already contains Custom Types. Do you want to continue pushing your local Slices?",
|
|
26
|
+
},
|
|
27
|
+
])
|
|
28
|
+
.then((res) => res.pushCustomTypes);
|
|
29
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import mime from "mime";
|
|
4
|
+
import snakeCase from "lodash.snakecase";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import FormData from "form-data";
|
|
7
|
+
import uniqid from "uniqid";
|
|
8
|
+
import { logs } from "../../utils";
|
|
9
|
+
import {
|
|
10
|
+
Component,
|
|
11
|
+
ComponentInfo,
|
|
12
|
+
VariationSM,
|
|
13
|
+
SliceSM,
|
|
14
|
+
} from "@slicemachine/core/build/models";
|
|
15
|
+
|
|
16
|
+
export type ALC = {
|
|
17
|
+
values: {
|
|
18
|
+
url: string;
|
|
19
|
+
fields: Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
imgixEndpoint: string;
|
|
22
|
+
err: null | string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function createAcl(
|
|
26
|
+
address: string,
|
|
27
|
+
repository: string,
|
|
28
|
+
authorization: string
|
|
29
|
+
): Promise<ALC> {
|
|
30
|
+
return axios
|
|
31
|
+
.get<ALC>(address + "create", {
|
|
32
|
+
headers: {
|
|
33
|
+
repository,
|
|
34
|
+
Authorization: `Bearer ${authorization}`,
|
|
35
|
+
"User-Agent": "slice-machine",
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
.then((res) => res.data);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function createFormForS3(
|
|
42
|
+
key: string,
|
|
43
|
+
filename: string,
|
|
44
|
+
filePath: string,
|
|
45
|
+
acl: ALC
|
|
46
|
+
): Promise<FormData | null> {
|
|
47
|
+
const form = new FormData();
|
|
48
|
+
Object.entries(acl.values.fields).forEach(([k, value]) => {
|
|
49
|
+
form.append(k, value);
|
|
50
|
+
});
|
|
51
|
+
form.append("key", key);
|
|
52
|
+
const contentType = mime.getType(filePath);
|
|
53
|
+
contentType && form.append("Content-Type", contentType);
|
|
54
|
+
|
|
55
|
+
return fs.promises
|
|
56
|
+
.readFile(filePath)
|
|
57
|
+
.then((file) => {
|
|
58
|
+
form.append("file", file, { filename });
|
|
59
|
+
return form;
|
|
60
|
+
})
|
|
61
|
+
.catch(() => {
|
|
62
|
+
logs.writeError(`Error reading preview image: ${filename}`);
|
|
63
|
+
return null;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createS3Key(
|
|
68
|
+
repository: string,
|
|
69
|
+
sliceName: string,
|
|
70
|
+
variationId: string,
|
|
71
|
+
filename: string
|
|
72
|
+
): string {
|
|
73
|
+
return `${repository}/shared-slices/${snakeCase(sliceName)}/${snakeCase(
|
|
74
|
+
variationId
|
|
75
|
+
)}-${uniqid()}/${filename}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function sendVariationPreviewToS3(
|
|
79
|
+
acl: ALC,
|
|
80
|
+
repository: string,
|
|
81
|
+
sliceName: string,
|
|
82
|
+
variationId: string,
|
|
83
|
+
filePath: string
|
|
84
|
+
): Promise<string | null> {
|
|
85
|
+
const filename = path.basename(filePath);
|
|
86
|
+
const key = createS3Key(repository, sliceName, variationId, filename);
|
|
87
|
+
const form = await createFormForS3(key, filename, filePath, acl);
|
|
88
|
+
if (form === null) return null;
|
|
89
|
+
if (form.hasKnownLength() === false) {
|
|
90
|
+
logs.writeError(
|
|
91
|
+
`[slice/push] An error occurred while uploading preview image ${filePath} as length in unknown`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const errorMessage = `[slice/push] An error occurred while uploading preview images for ${sliceName}-${variationId} - please contact support`;
|
|
96
|
+
|
|
97
|
+
return axios
|
|
98
|
+
.post(acl.values.url, form, {
|
|
99
|
+
headers: {
|
|
100
|
+
...form.getHeaders(),
|
|
101
|
+
"Content-Length": String(form.getLengthSync()),
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
.then((res) => {
|
|
105
|
+
if (res.status !== 204) {
|
|
106
|
+
logs.writeError(errorMessage);
|
|
107
|
+
logs.writeError(`${res.status}: ${res.statusText}`);
|
|
108
|
+
return null;
|
|
109
|
+
} else {
|
|
110
|
+
return `${acl.imgixEndpoint}/${key}`;
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
.catch((err) => {
|
|
114
|
+
logs.writeError(errorMessage);
|
|
115
|
+
if (axios.isAxiosError(err) && err.response) {
|
|
116
|
+
logs.writeError(`${err.response.status}: ${err.response.statusText}`);
|
|
117
|
+
} else if (err instanceof Error) {
|
|
118
|
+
logs.writeError(err.message);
|
|
119
|
+
} else {
|
|
120
|
+
logs.writeError(String(err));
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function maybeAddImageUrlToVariation(
|
|
127
|
+
acl: ALC,
|
|
128
|
+
repository: string,
|
|
129
|
+
modelId: string,
|
|
130
|
+
pathToScreenShot: string,
|
|
131
|
+
variation: VariationSM
|
|
132
|
+
): Promise<VariationSM> {
|
|
133
|
+
const imageUrl = await sendVariationPreviewToS3(
|
|
134
|
+
acl,
|
|
135
|
+
repository,
|
|
136
|
+
modelId,
|
|
137
|
+
variation.id,
|
|
138
|
+
pathToScreenShot
|
|
139
|
+
);
|
|
140
|
+
if (!imageUrl) return variation;
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
...variation,
|
|
144
|
+
imageUrl,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function addImageUrlsToVariations(
|
|
149
|
+
acl: ALC,
|
|
150
|
+
repository: string,
|
|
151
|
+
modelId: string,
|
|
152
|
+
screenshotPaths: ComponentInfo["screenshotPaths"],
|
|
153
|
+
variations: Array<VariationSM>
|
|
154
|
+
): Promise<Array<VariationSM>> {
|
|
155
|
+
return Promise.all(
|
|
156
|
+
variations.map(async (variation) => {
|
|
157
|
+
const screenshot = screenshotPaths[variation.id];
|
|
158
|
+
if (!screenshot || !screenshot.path) return variation;
|
|
159
|
+
|
|
160
|
+
return maybeAddImageUrlToVariation(
|
|
161
|
+
acl,
|
|
162
|
+
repository,
|
|
163
|
+
modelId,
|
|
164
|
+
screenshot.path,
|
|
165
|
+
variation
|
|
166
|
+
);
|
|
167
|
+
})
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function maybeUpdateModelVariationsWithImageUrl(
|
|
172
|
+
acl: ALC,
|
|
173
|
+
repository: string,
|
|
174
|
+
component: Component
|
|
175
|
+
): Promise<SliceSM> {
|
|
176
|
+
const { screenshotPaths, model } = component;
|
|
177
|
+
const variations = await addImageUrlsToVariations(
|
|
178
|
+
acl,
|
|
179
|
+
repository,
|
|
180
|
+
model.id,
|
|
181
|
+
screenshotPaths,
|
|
182
|
+
model.variations
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
...model,
|
|
187
|
+
variations,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function addImageUrlsToModelVariations(
|
|
192
|
+
acl: ALC,
|
|
193
|
+
repository: string,
|
|
194
|
+
components: Array<Component>
|
|
195
|
+
): Promise<Array<SliceSM>> {
|
|
196
|
+
return Promise.all(
|
|
197
|
+
components.map(async (component) =>
|
|
198
|
+
maybeUpdateModelVariationsWithImageUrl(acl, repository, component)
|
|
199
|
+
)
|
|
200
|
+
);
|
|
201
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Component } from "@slicemachine/core/build/models";
|
|
2
|
+
import * as Libraries from "@slicemachine/core/build/libraries";
|
|
3
|
+
import { logs } from "../../utils";
|
|
4
|
+
import { getRemoteSliceIds, sendManyModelsToPrismic } from "./communication";
|
|
5
|
+
import { getEndpointsFromBase } from "./endpoints";
|
|
6
|
+
import { promptToPushSlices } from "./prompts";
|
|
7
|
+
import { addImageUrlsToModelVariations, createAcl } from "./s3";
|
|
8
|
+
|
|
9
|
+
export async function sendSlicesFromStarter(
|
|
10
|
+
base: string,
|
|
11
|
+
repository: string,
|
|
12
|
+
authorization: string,
|
|
13
|
+
libraryPaths: Array<string>,
|
|
14
|
+
cwd: string
|
|
15
|
+
) {
|
|
16
|
+
const endpoints = getEndpointsFromBase(base);
|
|
17
|
+
const libraries = Libraries.libraries(cwd, libraryPaths);
|
|
18
|
+
|
|
19
|
+
if (libraries.length === 0) return Promise.resolve(false);
|
|
20
|
+
|
|
21
|
+
const remoteSlices = await getRemoteSliceIds(
|
|
22
|
+
endpoints.Models,
|
|
23
|
+
repository,
|
|
24
|
+
authorization
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (remoteSlices.length) {
|
|
28
|
+
// do prompt about slices
|
|
29
|
+
|
|
30
|
+
const pushAnyway = await promptToPushSlices();
|
|
31
|
+
|
|
32
|
+
if (pushAnyway === false) return Promise.resolve(true);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const spinner = logs.spinner(
|
|
36
|
+
"Pushing existing Slice models to your repository"
|
|
37
|
+
);
|
|
38
|
+
spinner.start();
|
|
39
|
+
|
|
40
|
+
const acl = await createAcl(endpoints.AclProvider, repository, authorization);
|
|
41
|
+
|
|
42
|
+
const components = libraries.reduce<Array<Component>>((acc, lib) => {
|
|
43
|
+
return [...acc, ...lib.components];
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
const models = await addImageUrlsToModelVariations(
|
|
47
|
+
acl,
|
|
48
|
+
repository,
|
|
49
|
+
components
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await sendManyModelsToPrismic(
|
|
53
|
+
repository,
|
|
54
|
+
authorization,
|
|
55
|
+
endpoints.Models,
|
|
56
|
+
remoteSlices,
|
|
57
|
+
models
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
spinner.succeed();
|
|
61
|
+
return Promise.resolve(true);
|
|
62
|
+
}
|