@appsemble/cli 0.20.13 → 0.20.14
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/README.md +4 -4
- package/__fixtures__/getBlockConfigFromTypeScript/comments/tsconfig.json +1 -1
- package/__fixtures__/getBlockConfigFromTypeScript/duplicateActions/tsconfig.json +1 -1
- package/__fixtures__/getBlockConfigFromTypeScript/duplicateEventEmitters/tsconfig.json +1 -1
- package/__fixtures__/getBlockConfigFromTypeScript/duplicateEventListeners/tsconfig.json +1 -1
- package/__fixtures__/getBlockConfigFromTypeScript/duplicateParameters/tsconfig.json +1 -1
- package/__fixtures__/getBlockConfigFromTypeScript/fontawesomeParameters/tsconfig.json +1 -1
- package/__fixtures__/getBlockConfigFromTypeScript/tsError/tsconfig.json +1 -1
- package/__fixtures__/getBlockConfigFromTypeScript/valid/tsconfig.json +1 -1
- package/commands/app/create.d.ts +1 -1
- package/commands/app/create.js +16 -24
- package/commands/app/create.js.map +1 -1
- package/commands/app/create.ts +3 -3
- package/commands/app/extract-messages.d.ts +1 -1
- package/commands/app/extract-messages.js +12 -20
- package/commands/app/extract-messages.js.map +1 -1
- package/commands/app/extract-messages.ts +2 -2
- package/commands/app/index.js +12 -36
- package/commands/app/index.js.map +1 -1
- package/commands/app/index.ts +9 -5
- package/commands/app/update.d.ts +1 -1
- package/commands/app/update.js +18 -26
- package/commands/app/update.js.map +1 -1
- package/commands/app/update.ts +4 -4
- package/commands/asset/create.d.ts +1 -1
- package/commands/asset/create.js +20 -28
- package/commands/asset/create.js.map +1 -1
- package/commands/asset/create.ts +4 -4
- package/commands/asset/index.js +5 -33
- package/commands/asset/index.js.map +1 -1
- package/commands/asset/index.ts +3 -3
- package/commands/block/build.d.ts +1 -1
- package/commands/block/build.js +14 -22
- package/commands/block/build.js.map +1 -1
- package/commands/block/build.ts +2 -2
- package/commands/block/extract-messages.d.ts +1 -1
- package/commands/block/extract-messages.js +15 -23
- package/commands/block/extract-messages.js.map +1 -1
- package/commands/block/extract-messages.ts +2 -2
- package/commands/block/index.js +12 -36
- package/commands/block/index.js.map +1 -1
- package/commands/block/index.ts +9 -5
- package/commands/block/publish.d.ts +1 -1
- package/commands/block/publish.js +17 -25
- package/commands/block/publish.js.map +1 -1
- package/commands/block/publish.ts +3 -3
- package/commands/cleanupResources.d.ts +1 -1
- package/commands/cleanupResources.js +6 -11
- package/commands/cleanupResources.js.map +1 -1
- package/commands/cleanupResources.ts +2 -2
- package/commands/config/get.d.ts +1 -1
- package/commands/config/get.js +10 -17
- package/commands/config/get.js.map +1 -1
- package/commands/config/get.ts +6 -5
- package/commands/config/index.js +10 -35
- package/commands/config/index.js.map +1 -1
- package/commands/config/index.ts +7 -4
- package/commands/config/set.d.ts +1 -1
- package/commands/config/set.js +15 -22
- package/commands/config/set.js.map +1 -1
- package/commands/config/set.ts +7 -6
- package/commands/login.d.ts +1 -1
- package/commands/login.js +3 -7
- package/commands/login.js.map +1 -1
- package/commands/login.ts +1 -1
- package/commands/logout.d.ts +1 -1
- package/commands/logout.js +3 -7
- package/commands/logout.js.map +1 -1
- package/commands/logout.ts +1 -1
- package/commands/migrate.d.ts +1 -1
- package/commands/migrate.js +8 -16
- package/commands/migrate.js.map +1 -1
- package/commands/migrate.ts +3 -3
- package/commands/organization/create.d.ts +1 -1
- package/commands/organization/create.js +10 -15
- package/commands/organization/create.js.map +1 -1
- package/commands/organization/create.ts +4 -4
- package/commands/organization/index.js +10 -35
- package/commands/organization/index.js.map +1 -1
- package/commands/organization/index.ts +7 -4
- package/commands/organization/update.d.ts +1 -1
- package/commands/organization/update.js +10 -15
- package/commands/organization/update.js.map +1 -1
- package/commands/organization/update.ts +4 -4
- package/commands/resource/create.d.ts +1 -1
- package/commands/resource/create.js +18 -26
- package/commands/resource/create.js.map +1 -1
- package/commands/resource/create.ts +4 -4
- package/commands/resource/index.js +10 -35
- package/commands/resource/index.js.map +1 -1
- package/commands/resource/index.ts +7 -4
- package/commands/resource/update.d.ts +1 -1
- package/commands/resource/update.js +18 -26
- package/commands/resource/update.js.map +1 -1
- package/commands/resource/update.ts +4 -4
- package/commands/runCronJobs.d.ts +1 -1
- package/commands/runCronJobs.js +6 -11
- package/commands/runCronJobs.js.map +1 -1
- package/commands/runCronJobs.ts +2 -2
- package/commands/start.d.ts +1 -1
- package/commands/start.js +10 -15
- package/commands/start.js.map +1 -1
- package/commands/start.ts +4 -4
- package/commands/team/create.d.ts +1 -1
- package/commands/team/create.js +10 -15
- package/commands/team/create.js.map +1 -1
- package/commands/team/create.ts +4 -4
- package/commands/team/delete.d.ts +1 -1
- package/commands/team/delete.js +10 -15
- package/commands/team/delete.js.map +1 -1
- package/commands/team/delete.ts +4 -4
- package/commands/team/index.js +14 -37
- package/commands/team/index.js.map +1 -1
- package/commands/team/index.ts +11 -6
- package/commands/team/member/delete.d.ts +1 -1
- package/commands/team/member/delete.js +10 -15
- package/commands/team/member/delete.js.map +1 -1
- package/commands/team/member/delete.ts +4 -4
- package/commands/team/member/index.js +12 -36
- package/commands/team/member/index.js.map +1 -1
- package/commands/team/member/index.ts +9 -5
- package/commands/team/member/invite.d.ts +1 -1
- package/commands/team/member/invite.js +10 -15
- package/commands/team/member/invite.js.map +1 -1
- package/commands/team/member/invite.ts +4 -4
- package/commands/team/member/update.d.ts +1 -1
- package/commands/team/member/update.js +12 -17
- package/commands/team/member/update.js.map +1 -1
- package/commands/team/member/update.ts +4 -4
- package/commands/team/update.d.ts +1 -1
- package/commands/team/update.js +10 -15
- package/commands/team/update.js.map +1 -1
- package/commands/team/update.ts +4 -4
- package/index.js +66 -94
- package/index.js.map +1 -1
- package/index.ts +65 -65
- package/jest.config.js +3 -1
- package/jest.setup.js +3 -5
- package/jest.setup.js.map +1 -1
- package/jest.setup.ts +1 -1
- package/lib/__snapshots__/getBlockConfigFromTypeScript.test.ts.snap +5 -6
- package/lib/app.d.ts +1 -1
- package/lib/app.js +151 -163
- package/lib/app.js.map +1 -1
- package/lib/app.ts +7 -7
- package/lib/asset.js +11 -18
- package/lib/asset.js.map +1 -1
- package/lib/authentication.d.ts +1 -1
- package/lib/authentication.js +34 -65
- package/lib/authentication.js.map +1 -1
- package/lib/authentication.ts +3 -2
- package/lib/block.js +99 -113
- package/lib/block.js.map +1 -1
- package/lib/block.test.js +9 -14
- package/lib/block.test.js.map +1 -1
- package/lib/block.test.ts +1 -1
- package/lib/block.ts +5 -5
- package/lib/coercers.js +5 -13
- package/lib/coercers.js.map +1 -1
- package/lib/coercers.test.js +2 -4
- package/lib/coercers.test.js.map +1 -1
- package/lib/coercers.test.ts +1 -1
- package/lib/getBlockConfigFromTypeScript.js +49 -56
- package/lib/getBlockConfigFromTypeScript.js.map +1 -1
- package/lib/getBlockConfigFromTypeScript.test.js +42 -46
- package/lib/getBlockConfigFromTypeScript.test.js.map +1 -1
- package/lib/getBlockConfigFromTypeScript.test.ts +7 -6
- package/lib/initAxios.d.ts +1 -1
- package/lib/initAxios.js +7 -14
- package/lib/initAxios.js.map +1 -1
- package/lib/initAxios.ts +2 -2
- package/lib/loadWebpackConfig.js +14 -45
- package/lib/loadWebpackConfig.js.map +1 -1
- package/lib/loadWebpackConfig.ts +10 -17
- package/lib/organization.js +23 -31
- package/lib/organization.js.map +1 -1
- package/lib/output.js +5 -9
- package/lib/output.js.map +1 -1
- package/lib/processCss.js +11 -18
- package/lib/processCss.js.map +1 -1
- package/lib/resource.js +17 -25
- package/lib/resource.js.map +1 -1
- package/lib/serverImport.js +6 -34
- package/lib/serverImport.js.map +1 -1
- package/lib/serverImport.ts +0 -1
- package/lib/team.js +28 -41
- package/lib/team.js.map +1 -1
- package/package.json +11 -10
- package/tsconfig.json +0 -1
- package/types.js +1 -2
package/lib/app.js
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const authentication_1 = require("./authentication");
|
|
16
|
-
const block_1 = require("./block");
|
|
17
|
-
const coercers_1 = require("./coercers");
|
|
18
|
-
const output_1 = require("./output");
|
|
19
|
-
const processCss_1 = require("./processCss");
|
|
20
|
-
const resource_1 = require("./resource");
|
|
1
|
+
import { createReadStream, existsSync } from 'fs';
|
|
2
|
+
import { mkdir, readdir, readFile, stat } from 'fs/promises';
|
|
3
|
+
import { join, parse, relative, resolve } from 'path';
|
|
4
|
+
import { inspect } from 'util';
|
|
5
|
+
import { AppsembleError, logger, opendirSafe, readData, writeData } from '@appsemble/node-utils';
|
|
6
|
+
import { extractAppMessages, has, normalizeBlockName } from '@appsemble/utils';
|
|
7
|
+
import axios from 'axios';
|
|
8
|
+
import FormData from 'form-data';
|
|
9
|
+
import { authenticate } from './authentication.js';
|
|
10
|
+
import { traverseBlockThemes } from './block.js';
|
|
11
|
+
import { coerceRemote } from './coercers.js';
|
|
12
|
+
import { printAxiosError } from './output.js';
|
|
13
|
+
import { processCss } from './processCss.js';
|
|
14
|
+
import { createResource } from './resource.js';
|
|
21
15
|
/**
|
|
22
16
|
* Traverses an app directory and appends the files it finds to the given FormData object.
|
|
23
17
|
*
|
|
@@ -26,33 +20,33 @@ const resource_1 = require("./resource");
|
|
|
26
20
|
* @param formData The FormData object to append the results into..
|
|
27
21
|
* @returns The context from `.appsemblerc.yaml` if a match was found.
|
|
28
22
|
*/
|
|
29
|
-
async function traverseAppDirectory(path, context, formData) {
|
|
23
|
+
export async function traverseAppDirectory(path, context, formData) {
|
|
30
24
|
let discoveredContext;
|
|
31
25
|
let rc;
|
|
32
26
|
let iconPath;
|
|
33
27
|
let maskableIconPath;
|
|
34
28
|
let yaml;
|
|
35
|
-
|
|
36
|
-
await
|
|
29
|
+
logger.info(`Traversing directory for App files in ${path} 🕵`);
|
|
30
|
+
await opendirSafe(path, async (filepath, filestat) => {
|
|
37
31
|
switch (filestat.name.toLowerCase()) {
|
|
38
32
|
case '.appsemblerc.yaml': {
|
|
39
|
-
|
|
40
|
-
[rc] = await
|
|
33
|
+
logger.info(`Reading app settings from ${filepath}`);
|
|
34
|
+
[rc] = await readData(filepath);
|
|
41
35
|
if ('iconBackground' in rc) {
|
|
42
36
|
formData.append('iconBackground', rc.iconBackground);
|
|
43
37
|
}
|
|
44
|
-
if (context &&
|
|
38
|
+
if (context && has(rc === null || rc === void 0 ? void 0 : rc.context, context)) {
|
|
45
39
|
discoveredContext = rc.context[context];
|
|
46
|
-
|
|
40
|
+
logger.verbose(`Using context: ${inspect(discoveredContext, { colors: true })}`);
|
|
47
41
|
}
|
|
48
42
|
break;
|
|
49
43
|
}
|
|
50
44
|
case 'app-definition.yaml': {
|
|
51
|
-
|
|
45
|
+
logger.info(`Using app definition from ${filepath}`);
|
|
52
46
|
if (yaml !== undefined) {
|
|
53
|
-
throw new
|
|
47
|
+
throw new AppsembleError('Found duplicate app definition');
|
|
54
48
|
}
|
|
55
|
-
const [, data] = await
|
|
49
|
+
const [, data] = await readData(filepath);
|
|
56
50
|
yaml = data;
|
|
57
51
|
formData.append('yaml', data);
|
|
58
52
|
return;
|
|
@@ -65,46 +59,45 @@ async function traverseAppDirectory(path, context, formData) {
|
|
|
65
59
|
maskableIconPath = filepath;
|
|
66
60
|
return;
|
|
67
61
|
case 'readme.md':
|
|
68
|
-
|
|
69
|
-
formData.append('longDescription', await
|
|
62
|
+
logger.info(`Including longDescription from ${filepath}`);
|
|
63
|
+
formData.append('longDescription', await readFile(filepath, 'utf8'));
|
|
70
64
|
return;
|
|
71
65
|
case 'screenshots':
|
|
72
|
-
return
|
|
73
|
-
|
|
66
|
+
return opendirSafe(filepath, (screenshotPath, screenshotStat) => {
|
|
67
|
+
logger.info(`Adding screenshot ${screenshotPath} 🖼️`);
|
|
74
68
|
if (!screenshotStat.isFile()) {
|
|
75
|
-
throw new
|
|
69
|
+
throw new AppsembleError(`Expected ${screenshotPath} to be an image file`);
|
|
76
70
|
}
|
|
77
|
-
formData.append('screenshots',
|
|
71
|
+
formData.append('screenshots', createReadStream(screenshotPath));
|
|
78
72
|
}, { allowMissing: true });
|
|
79
73
|
case 'theme':
|
|
80
|
-
return
|
|
74
|
+
return opendirSafe(filepath, async (themeDir, themeStat) => {
|
|
81
75
|
const name = themeStat.name.toLowerCase();
|
|
82
76
|
if (name !== 'core' && name !== 'shared') {
|
|
83
77
|
return;
|
|
84
78
|
}
|
|
85
79
|
if (!themeStat.isDirectory()) {
|
|
86
|
-
throw new
|
|
80
|
+
throw new AppsembleError(`Expected ${themeDir} to be a directory`);
|
|
87
81
|
}
|
|
88
|
-
const css = await
|
|
82
|
+
const css = await processCss(join(themeDir, 'index.css'));
|
|
89
83
|
formData.append(`${name}Style`, css);
|
|
90
84
|
});
|
|
91
85
|
default:
|
|
92
|
-
|
|
86
|
+
logger.warn(`Found unused file ${filepath}`);
|
|
93
87
|
}
|
|
94
88
|
});
|
|
95
89
|
if (yaml === undefined) {
|
|
96
|
-
throw new
|
|
90
|
+
throw new AppsembleError('No app definition found');
|
|
97
91
|
}
|
|
98
92
|
discoveredContext || (discoveredContext = {});
|
|
99
93
|
discoveredContext.icon = discoveredContext.icon
|
|
100
|
-
?
|
|
94
|
+
? resolve(path, discoveredContext.icon)
|
|
101
95
|
: iconPath;
|
|
102
96
|
discoveredContext.maskableIcon = discoveredContext.maskableIcon
|
|
103
|
-
?
|
|
97
|
+
? resolve(path, discoveredContext.maskableIcon)
|
|
104
98
|
: maskableIconPath;
|
|
105
99
|
return [discoveredContext, rc, yaml];
|
|
106
100
|
}
|
|
107
|
-
exports.traverseAppDirectory = traverseAppDirectory;
|
|
108
101
|
/**
|
|
109
102
|
* Upload messages for an app.
|
|
110
103
|
*
|
|
@@ -113,27 +106,26 @@ exports.traverseAppDirectory = traverseAppDirectory;
|
|
|
113
106
|
* @param remote The remote to upload the messages to.
|
|
114
107
|
* @param force Whether or not to force the update for locked apps.
|
|
115
108
|
*/
|
|
116
|
-
async function uploadMessages(path, appId, remote, force) {
|
|
109
|
+
export async function uploadMessages(path, appId, remote, force) {
|
|
117
110
|
const result = [];
|
|
118
|
-
|
|
119
|
-
await
|
|
120
|
-
|
|
121
|
-
const { name: language } =
|
|
111
|
+
logger.info('Searching for translations 🕵');
|
|
112
|
+
await opendirSafe(join(path, 'i18n'), async (messageFile) => {
|
|
113
|
+
logger.verbose(`Processing ${messageFile} ⚙️`);
|
|
114
|
+
const { name: language } = parse(messageFile);
|
|
122
115
|
if (result.some((entry) => entry.language === language)) {
|
|
123
|
-
throw new
|
|
116
|
+
throw new AppsembleError(`Found duplicate language “${language}”. Make sure each language only exists once in the directory.`);
|
|
124
117
|
}
|
|
125
|
-
const [messages] = await
|
|
118
|
+
const [messages] = await readData(messageFile);
|
|
126
119
|
result.push({ force, language, messages });
|
|
127
120
|
}, { allowMissing: true });
|
|
128
121
|
if (!result.length) {
|
|
129
|
-
|
|
122
|
+
logger.info('No translations found 🤷');
|
|
130
123
|
}
|
|
131
124
|
for (const language of result) {
|
|
132
|
-
await
|
|
133
|
-
|
|
125
|
+
await axios.post(`/api/apps/${appId}/messages`, language, { baseURL: remote });
|
|
126
|
+
logger.info(`Successfully uploaded messages for language “${language.language}” 🎉`);
|
|
134
127
|
}
|
|
135
128
|
}
|
|
136
|
-
exports.uploadMessages = uploadMessages;
|
|
137
129
|
/**
|
|
138
130
|
* @param path The path to the app directory.
|
|
139
131
|
* @param languages A list of languages for which translations should be added in addition to the
|
|
@@ -141,24 +133,24 @@ exports.uploadMessages = uploadMessages;
|
|
|
141
133
|
* @param verify A list of languages to verify.
|
|
142
134
|
* @param format The file format that should be used for the output.
|
|
143
135
|
*/
|
|
144
|
-
async function writeAppMessages(path, languages, verify, format) {
|
|
136
|
+
export async function writeAppMessages(path, languages, verify, format) {
|
|
145
137
|
var _a, _b;
|
|
146
|
-
|
|
138
|
+
logger.info(`Extracting messages from ${path}`);
|
|
147
139
|
let app;
|
|
148
|
-
let i18nDir =
|
|
140
|
+
let i18nDir = join(path, 'i18n');
|
|
149
141
|
const messageFiles = new Set();
|
|
150
|
-
await
|
|
142
|
+
await opendirSafe(path, async (filepath, filestat) => {
|
|
151
143
|
switch (filestat.name.toLowerCase()) {
|
|
152
144
|
case 'app-definition.yaml': {
|
|
153
|
-
[app] = await
|
|
145
|
+
[app] = await readData(filepath);
|
|
154
146
|
break;
|
|
155
147
|
}
|
|
156
148
|
case 'i18n': {
|
|
157
149
|
// For case insensitivity
|
|
158
150
|
i18nDir = filepath;
|
|
159
|
-
const i18nFiles = await
|
|
151
|
+
const i18nFiles = await readdir(filepath);
|
|
160
152
|
for (const f of i18nFiles) {
|
|
161
|
-
messageFiles.add(
|
|
153
|
+
messageFiles.add(join(filepath, f));
|
|
162
154
|
}
|
|
163
155
|
break;
|
|
164
156
|
}
|
|
@@ -167,18 +159,18 @@ async function writeAppMessages(path, languages, verify, format) {
|
|
|
167
159
|
}
|
|
168
160
|
});
|
|
169
161
|
if (!app) {
|
|
170
|
-
throw new
|
|
162
|
+
throw new AppsembleError(`Couldn’t find app definition for ${path}`);
|
|
171
163
|
}
|
|
172
164
|
// Ensure the i18n directory exists.
|
|
173
|
-
await
|
|
165
|
+
await mkdir(i18nDir, { recursive: true });
|
|
174
166
|
for (const lang of [...languages, ...verify]) {
|
|
175
|
-
messageFiles.add(
|
|
167
|
+
messageFiles.add(join(i18nDir, `${lang}.${format}`));
|
|
176
168
|
}
|
|
177
|
-
const defaultLangFile =
|
|
169
|
+
const defaultLangFile = join(i18nDir, `${app.defaultLanguage || 'en'}.${format}`);
|
|
178
170
|
messageFiles.add(defaultLangFile);
|
|
179
171
|
const blockMessageKeys = {};
|
|
180
|
-
const extractedMessages =
|
|
181
|
-
const type =
|
|
172
|
+
const extractedMessages = extractAppMessages(app, (block) => {
|
|
173
|
+
const type = normalizeBlockName(block.type);
|
|
182
174
|
if (blockMessageKeys[type]) {
|
|
183
175
|
blockMessageKeys[type][block.version] = {};
|
|
184
176
|
}
|
|
@@ -188,15 +180,15 @@ async function writeAppMessages(path, languages, verify, format) {
|
|
|
188
180
|
};
|
|
189
181
|
}
|
|
190
182
|
});
|
|
191
|
-
|
|
183
|
+
logger.verbose(`Found message IDs: ${inspect(extractedMessages)}`);
|
|
192
184
|
for (const filepath of messageFiles) {
|
|
193
|
-
|
|
185
|
+
logger.info(`Processing ${filepath}`);
|
|
194
186
|
let oldMessages;
|
|
195
|
-
if (
|
|
196
|
-
[oldMessages] = await
|
|
187
|
+
if (existsSync(filepath)) {
|
|
188
|
+
[oldMessages] = await readData(filepath);
|
|
197
189
|
}
|
|
198
190
|
else if (verify) {
|
|
199
|
-
throw new
|
|
191
|
+
throw new AppsembleError(`Missing translations file: ${filepath}`);
|
|
200
192
|
}
|
|
201
193
|
else {
|
|
202
194
|
oldMessages = {
|
|
@@ -206,29 +198,29 @@ async function writeAppMessages(path, languages, verify, format) {
|
|
|
206
198
|
};
|
|
207
199
|
}
|
|
208
200
|
const newMessageIds = Object.fromEntries(Object.keys(extractedMessages.messageIds).map((key) => {
|
|
209
|
-
if (
|
|
201
|
+
if (has(oldMessages.messageIds, key) && oldMessages.messageIds[key]) {
|
|
210
202
|
if (typeof oldMessages.messageIds[key] !== 'string') {
|
|
211
|
-
throw new
|
|
203
|
+
throw new AppsembleError(`Invalid translation key: messageIds.${key}`);
|
|
212
204
|
}
|
|
213
205
|
return [key, oldMessages.messageIds[key]];
|
|
214
206
|
}
|
|
215
|
-
if (verify.includes(
|
|
216
|
-
throw new
|
|
207
|
+
if (verify.includes(parse(filepath).name)) {
|
|
208
|
+
throw new AppsembleError(`Missing translation: messageIds.${key}`);
|
|
217
209
|
}
|
|
218
210
|
return [key, ''];
|
|
219
211
|
}));
|
|
220
212
|
const newAppMessages = Object.fromEntries(Object.keys(extractedMessages.app).map((key) => {
|
|
221
|
-
if (
|
|
213
|
+
if (has(oldMessages.app, key) && oldMessages.app[key]) {
|
|
222
214
|
if (typeof oldMessages.app[key] !== 'string') {
|
|
223
|
-
throw new
|
|
215
|
+
throw new AppsembleError(`Invalid translation key: app.${key}`);
|
|
224
216
|
}
|
|
225
217
|
return [key, oldMessages.app[key]];
|
|
226
218
|
}
|
|
227
219
|
if (filepath === defaultLangFile) {
|
|
228
220
|
return [key, extractedMessages.app[key]];
|
|
229
221
|
}
|
|
230
|
-
if (verify.includes(
|
|
231
|
-
throw new
|
|
222
|
+
if (verify.includes(parse(filepath).name)) {
|
|
223
|
+
throw new AppsembleError(`Missing translation: app.${key}`);
|
|
232
224
|
}
|
|
233
225
|
return [key, ''];
|
|
234
226
|
}));
|
|
@@ -245,14 +237,14 @@ async function writeAppMessages(path, languages, verify, format) {
|
|
|
245
237
|
const coreMessages = (_a = oldMessages.core) !== null && _a !== void 0 ? _a : {};
|
|
246
238
|
for (const [key, value] of Object.entries(coreMessages)) {
|
|
247
239
|
if (!value || typeof value !== 'string') {
|
|
248
|
-
throw new
|
|
240
|
+
throw new AppsembleError(`Invalid translation key: core.${key}`);
|
|
249
241
|
}
|
|
250
242
|
}
|
|
251
243
|
const blockMessages = {};
|
|
252
244
|
if (oldMessages.blocks) {
|
|
253
245
|
for (const key of Object.keys(oldMessages.blocks)) {
|
|
254
|
-
if (!
|
|
255
|
-
throw new
|
|
246
|
+
if (!has(blockMessageKeys, key)) {
|
|
247
|
+
throw new AppsembleError(`Invalid translation key: blocks.${key}\nThis block is not used in the app`);
|
|
256
248
|
}
|
|
257
249
|
}
|
|
258
250
|
}
|
|
@@ -260,16 +252,16 @@ async function writeAppMessages(path, languages, verify, format) {
|
|
|
260
252
|
if ((_b = oldMessages.blocks) === null || _b === void 0 ? void 0 : _b[blockName]) {
|
|
261
253
|
blockMessages[blockName] = {};
|
|
262
254
|
for (const [version, oldValues] of Object.entries(oldMessages.blocks[blockName])) {
|
|
263
|
-
if (!
|
|
264
|
-
throw new
|
|
255
|
+
if (!has(blockMessageKeys[blockName], version)) {
|
|
256
|
+
throw new AppsembleError(`Invalid translation key: blocks.${blockName}.${version}
|
|
265
257
|
This block version is not used in the app`);
|
|
266
258
|
}
|
|
267
259
|
for (const [oldValueKey, oldValue] of Object.entries(oldMessages.blocks[blockName][version])) {
|
|
268
260
|
if (typeof oldValue !== 'string') {
|
|
269
|
-
throw new
|
|
261
|
+
throw new AppsembleError(`Invalid translation key: blocks.${blockName}.${version}.${oldValueKey}`);
|
|
270
262
|
}
|
|
271
|
-
if (verify.includes(
|
|
272
|
-
throw new
|
|
263
|
+
if (verify.includes(parse(filepath).name) && !oldValue) {
|
|
264
|
+
throw new AppsembleError(`Missing translation: blocks.${blockName}.${version}.${oldValueKey}`);
|
|
273
265
|
}
|
|
274
266
|
}
|
|
275
267
|
blockMessages[blockName][version] = oldValues;
|
|
@@ -282,22 +274,21 @@ This block version is not used in the app`);
|
|
|
282
274
|
...(Object.keys(blockMessages).length && { blocks: blockMessages }),
|
|
283
275
|
...(Object.keys(coreMessages).length && { core: coreMessages }),
|
|
284
276
|
};
|
|
285
|
-
await
|
|
277
|
+
await writeData(filepath, result);
|
|
286
278
|
}
|
|
287
279
|
}
|
|
288
|
-
exports.writeAppMessages = writeAppMessages;
|
|
289
280
|
/**
|
|
290
281
|
* Update an existing app.
|
|
291
282
|
*
|
|
292
283
|
* @param argv The command line options used for updating the app.
|
|
293
284
|
*/
|
|
294
|
-
async function updateApp({ clientCredentials, context, force, path, ...options }) {
|
|
285
|
+
export async function updateApp({ clientCredentials, context, force, path, ...options }) {
|
|
295
286
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
296
|
-
const file = await
|
|
297
|
-
const formData = new
|
|
287
|
+
const file = await stat(path);
|
|
288
|
+
const formData = new FormData();
|
|
298
289
|
let appsembleContext;
|
|
299
290
|
if (file.isFile()) {
|
|
300
|
-
const [, data] = await
|
|
291
|
+
const [, data] = await readData(path);
|
|
301
292
|
formData.append('yaml', data);
|
|
302
293
|
}
|
|
303
294
|
else {
|
|
@@ -313,75 +304,74 @@ async function updateApp({ clientCredentials, context, force, path, ...options }
|
|
|
313
304
|
const sentryDsn = (_j = appsembleContext.sentryDsn) !== null && _j !== void 0 ? _j : options.sentryDsn;
|
|
314
305
|
const sentryEnvironment = (_k = appsembleContext.sentryEnvironment) !== null && _k !== void 0 ? _k : options.sentryEnvironment;
|
|
315
306
|
const googleAnalyticsId = (_l = appsembleContext.googleAnalyticsId) !== null && _l !== void 0 ? _l : options.googleAnalyticsId;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
307
|
+
logger.info(`App id: ${id}`);
|
|
308
|
+
logger.verbose(`App remote: ${remote}`);
|
|
309
|
+
logger.verbose(`App is template: ${inspect(template, { colors: true })}`);
|
|
310
|
+
logger.verbose(`App visibility: ${visibility}`);
|
|
311
|
+
logger.verbose(`Icon background: ${iconBackground}`);
|
|
312
|
+
logger.verbose(`Force update: ${inspect(force, { colors: true })}`);
|
|
322
313
|
if (!id) {
|
|
323
|
-
throw new
|
|
314
|
+
throw new AppsembleError('The app id must be passed as a command line flag or in the context');
|
|
324
315
|
}
|
|
325
316
|
formData.append('force', String(force));
|
|
326
317
|
formData.append('template', String(template));
|
|
327
318
|
formData.append('visibility', visibility);
|
|
328
319
|
formData.append('iconBackground', iconBackground);
|
|
329
320
|
if (icon) {
|
|
330
|
-
const realIcon = typeof icon === 'string' ?
|
|
331
|
-
|
|
321
|
+
const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
|
|
322
|
+
logger.info(`Using icon from ${(_m = realIcon.path) !== null && _m !== void 0 ? _m : 'stdin'}`);
|
|
332
323
|
formData.append('icon', realIcon);
|
|
333
324
|
}
|
|
334
325
|
if (maskableIcon) {
|
|
335
|
-
const realIcon = typeof maskableIcon === 'string' ?
|
|
336
|
-
|
|
326
|
+
const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
|
|
327
|
+
logger.info(`Using maskable icon from ${(_o = realIcon.path) !== null && _o !== void 0 ? _o : 'stdin'}`);
|
|
337
328
|
formData.append('maskableIcon', realIcon);
|
|
338
329
|
}
|
|
339
330
|
if (sentryDsn) {
|
|
340
|
-
|
|
331
|
+
logger.info(`Using custom Sentry DSN ${sentryEnvironment ? `with environment ${sentryEnvironment}` : ''}`);
|
|
341
332
|
formData.append('sentryDsn', sentryDsn);
|
|
342
333
|
if (sentryEnvironment) {
|
|
343
334
|
formData.append('sentryEnvironment', sentryEnvironment);
|
|
344
335
|
}
|
|
345
336
|
}
|
|
346
337
|
if (googleAnalyticsId) {
|
|
347
|
-
|
|
338
|
+
logger.info('Using Google Analytics');
|
|
348
339
|
formData.append('googleAnalyticsID', googleAnalyticsId);
|
|
349
340
|
}
|
|
350
|
-
await
|
|
351
|
-
const { data } = await
|
|
341
|
+
await authenticate(remote, 'apps:write', clientCredentials);
|
|
342
|
+
const { data } = await axios.patch(`/api/apps/${id}`, formData, { baseURL: remote });
|
|
352
343
|
if (file.isDirectory()) {
|
|
353
344
|
// After uploading the app, upload block styles and messages if they are available
|
|
354
|
-
await
|
|
345
|
+
await traverseBlockThemes(path, data.id, remote, force);
|
|
355
346
|
await uploadMessages(path, data.id, remote, force);
|
|
356
347
|
}
|
|
357
348
|
const { host, protocol } = new URL(remote);
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
349
|
+
logger.info(`Successfully updated app ${data.definition.name}! 🙌`);
|
|
350
|
+
logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
|
|
351
|
+
logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
|
|
361
352
|
}
|
|
362
|
-
exports.updateApp = updateApp;
|
|
363
353
|
/**
|
|
364
354
|
* Create a new App.
|
|
365
355
|
*
|
|
366
356
|
* @param options The options to use for creating an app.
|
|
367
357
|
*/
|
|
368
|
-
async function createApp({ clientCredentials, context, dryRun, modifyContext, path, resources, ...options }) {
|
|
358
|
+
export async function createApp({ clientCredentials, context, dryRun, modifyContext, path, resources, ...options }) {
|
|
369
359
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
370
|
-
const file = await
|
|
371
|
-
const formData = new
|
|
360
|
+
const file = await stat(path);
|
|
361
|
+
const formData = new FormData();
|
|
372
362
|
let appsembleContext;
|
|
373
363
|
let rc;
|
|
374
364
|
let yaml;
|
|
375
|
-
let filename =
|
|
365
|
+
let filename = relative(process.cwd(), path);
|
|
376
366
|
if (file.isFile()) {
|
|
377
367
|
// Assuming file is App YAML
|
|
378
|
-
const [, data] = await
|
|
368
|
+
const [, data] = await readData(path);
|
|
379
369
|
yaml = data;
|
|
380
370
|
formData.append('yaml', data);
|
|
381
371
|
}
|
|
382
372
|
else {
|
|
383
373
|
[appsembleContext, rc, yaml] = await traverseAppDirectory(path, context, formData);
|
|
384
|
-
filename =
|
|
374
|
+
filename = join(filename, 'app-definition.yaml');
|
|
385
375
|
}
|
|
386
376
|
const remote = (_a = appsembleContext.remote) !== null && _a !== void 0 ? _a : options.remote;
|
|
387
377
|
const organizationId = (_b = appsembleContext.organization) !== null && _b !== void 0 ? _b : options.organization;
|
|
@@ -393,87 +383,87 @@ async function createApp({ clientCredentials, context, dryRun, modifyContext, pa
|
|
|
393
383
|
const sentryDsn = (_j = appsembleContext.sentryDsn) !== null && _j !== void 0 ? _j : options.sentryDsn;
|
|
394
384
|
const sentryEnvironment = (_k = appsembleContext.sentryEnvironment) !== null && _k !== void 0 ? _k : options.sentryEnvironment;
|
|
395
385
|
const googleAnalyticsId = (_l = appsembleContext.googleAnalyticsId) !== null && _l !== void 0 ? _l : options.googleAnalyticsId;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
386
|
+
logger.verbose(`App remote: ${remote}`);
|
|
387
|
+
logger.verbose(`App organzation: ${organizationId}`);
|
|
388
|
+
logger.verbose(`App is template: ${inspect(template, { colors: true })}`);
|
|
389
|
+
logger.verbose(`App visibility: ${visibility}`);
|
|
390
|
+
logger.verbose(`Icon background: ${iconBackground}`);
|
|
401
391
|
if (!organizationId) {
|
|
402
|
-
throw new
|
|
392
|
+
throw new AppsembleError('An organization id must be passed as a command line flag or in the context');
|
|
403
393
|
}
|
|
404
394
|
formData.append('OrganizationId', organizationId);
|
|
405
395
|
formData.append('template', String(template));
|
|
406
396
|
formData.append('visibility', visibility);
|
|
407
397
|
formData.append('iconBackground', iconBackground);
|
|
408
398
|
if (icon) {
|
|
409
|
-
const realIcon = typeof icon === 'string' ?
|
|
410
|
-
|
|
399
|
+
const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
|
|
400
|
+
logger.info(`Using icon from ${(_m = realIcon.path) !== null && _m !== void 0 ? _m : 'stdin'}`);
|
|
411
401
|
formData.append('icon', realIcon);
|
|
412
402
|
}
|
|
413
403
|
if (maskableIcon) {
|
|
414
|
-
const realIcon = typeof maskableIcon === 'string' ?
|
|
415
|
-
|
|
404
|
+
const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
|
|
405
|
+
logger.info(`Using maskable icon from ${(_o = realIcon.path) !== null && _o !== void 0 ? _o : 'stdin'}`);
|
|
416
406
|
formData.append('maskableIcon', realIcon);
|
|
417
407
|
}
|
|
418
408
|
if (sentryDsn) {
|
|
419
|
-
|
|
409
|
+
logger.info(`Using custom Sentry DSN ${sentryEnvironment ? `with environment ${sentryEnvironment}` : ''}`);
|
|
420
410
|
formData.append('sentryDsn', sentryDsn);
|
|
421
411
|
if (sentryEnvironment) {
|
|
422
412
|
formData.append('sentryEnvironment', sentryEnvironment);
|
|
423
413
|
}
|
|
424
414
|
}
|
|
425
415
|
if (googleAnalyticsId) {
|
|
426
|
-
|
|
416
|
+
logger.info('Using Google Analytics');
|
|
427
417
|
formData.append('googleAnalyticsID', googleAnalyticsId);
|
|
428
418
|
}
|
|
429
|
-
await
|
|
419
|
+
await authenticate(remote, resources ? 'apps:write resources:write' : 'apps:write', clientCredentials);
|
|
430
420
|
let data;
|
|
431
421
|
try {
|
|
432
|
-
({ data } = await
|
|
422
|
+
({ data } = await axios.post('/api/apps', formData, {
|
|
433
423
|
baseURL: remote,
|
|
434
424
|
params: { dryRun: dryRun || undefined },
|
|
435
425
|
}));
|
|
436
426
|
}
|
|
437
427
|
catch (error) {
|
|
438
|
-
if (!
|
|
428
|
+
if (!axios.isAxiosError(error)) {
|
|
439
429
|
throw error;
|
|
440
430
|
}
|
|
441
431
|
if (((_q = (_p = error.response) === null || _p === void 0 ? void 0 : _p.data) === null || _q === void 0 ? void 0 : _q.message) !== 'App validation failed') {
|
|
442
432
|
throw error;
|
|
443
433
|
}
|
|
444
|
-
throw new
|
|
434
|
+
throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
|
|
445
435
|
}
|
|
446
436
|
if (dryRun) {
|
|
447
|
-
|
|
448
|
-
|
|
437
|
+
logger.info('Skipped uploading block themes and app messages.');
|
|
438
|
+
logger.info('Successfully ran dry run. The app should be valid when creating it.');
|
|
449
439
|
return;
|
|
450
440
|
}
|
|
451
441
|
if (file.isDirectory() && !dryRun) {
|
|
452
442
|
// After uploading the app, upload block styles and messages if they are available
|
|
453
|
-
await
|
|
443
|
+
await traverseBlockThemes(path, data.id, remote, false);
|
|
454
444
|
await uploadMessages(path, data.id, remote, false);
|
|
455
|
-
if (resources &&
|
|
456
|
-
const resourcePath =
|
|
445
|
+
if (resources && existsSync(join(path, 'resources'))) {
|
|
446
|
+
const resourcePath = join(path, 'resources');
|
|
457
447
|
try {
|
|
458
|
-
const resourceFiles = await
|
|
448
|
+
const resourceFiles = await readdir(resourcePath, { withFileTypes: true });
|
|
459
449
|
for (const resource of resourceFiles) {
|
|
460
450
|
if (resource.isFile()) {
|
|
461
|
-
const { name } =
|
|
462
|
-
await
|
|
451
|
+
const { name } = parse(resource.name);
|
|
452
|
+
await createResource({
|
|
463
453
|
appId: data.id,
|
|
464
|
-
path:
|
|
454
|
+
path: join(resourcePath, resource.name),
|
|
465
455
|
remote,
|
|
466
456
|
resourceName: name,
|
|
467
457
|
});
|
|
468
458
|
}
|
|
469
459
|
else if (resource.isDirectory()) {
|
|
470
|
-
const subDirectoryResources = await
|
|
460
|
+
const subDirectoryResources = await readdir(join(resourcePath, resource.name), {
|
|
471
461
|
withFileTypes: true,
|
|
472
462
|
});
|
|
473
463
|
for (const subResource of subDirectoryResources.filter((s) => s.isFile())) {
|
|
474
|
-
await
|
|
464
|
+
await createResource({
|
|
475
465
|
appId: data.id,
|
|
476
|
-
path:
|
|
466
|
+
path: join(resourcePath, resource.name, subResource.name),
|
|
477
467
|
remote,
|
|
478
468
|
resourceName: resource.name,
|
|
479
469
|
});
|
|
@@ -482,22 +472,21 @@ async function createApp({ clientCredentials, context, dryRun, modifyContext, pa
|
|
|
482
472
|
}
|
|
483
473
|
}
|
|
484
474
|
catch (error) {
|
|
485
|
-
|
|
486
|
-
|
|
475
|
+
logger.error('Something went wrong when creating resources:');
|
|
476
|
+
logger.error(error);
|
|
487
477
|
}
|
|
488
478
|
}
|
|
489
479
|
}
|
|
490
480
|
if (modifyContext && appsembleContext && context && !dryRun) {
|
|
491
481
|
rc.context[context].id = data.id;
|
|
492
|
-
await
|
|
493
|
-
|
|
482
|
+
await writeData(join(path, '.appsemblerc.yaml'), rc);
|
|
483
|
+
logger.info(`Updated .appsemblerc: Set context.${context}.id to ${data.id}`);
|
|
494
484
|
}
|
|
495
485
|
const { host, protocol } = new URL(remote);
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
486
|
+
logger.info(`Successfully created app ${data.definition.name}! 🙌`);
|
|
487
|
+
logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
|
|
488
|
+
logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
|
|
499
489
|
}
|
|
500
|
-
exports.createApp = createApp;
|
|
501
490
|
/**
|
|
502
491
|
* Resolve the app’s ID and remote based on the app context.
|
|
503
492
|
*
|
|
@@ -507,13 +496,13 @@ exports.createApp = createApp;
|
|
|
507
496
|
* @param defaultAppId The app ID to fall back to.
|
|
508
497
|
* @returns The resolved app ID and remote.
|
|
509
498
|
*/
|
|
510
|
-
async function resolveAppIdAndRemote(appPath, name, defaultRemote, defaultAppId) {
|
|
499
|
+
export async function resolveAppIdAndRemote(appPath, name, defaultRemote, defaultAppId) {
|
|
511
500
|
var _a;
|
|
512
501
|
let id = defaultAppId;
|
|
513
502
|
let resolvedRemote = defaultRemote;
|
|
514
503
|
if (appPath) {
|
|
515
|
-
const rcPath =
|
|
516
|
-
const [rc] = await
|
|
504
|
+
const rcPath = join(appPath, '.appsemblerc.yaml');
|
|
505
|
+
const [rc] = await readData(rcPath);
|
|
517
506
|
const context = (_a = rc.context) === null || _a === void 0 ? void 0 : _a[name];
|
|
518
507
|
if (!defaultAppId) {
|
|
519
508
|
id = context === null || context === void 0 ? void 0 : context.id;
|
|
@@ -523,9 +512,8 @@ async function resolveAppIdAndRemote(appPath, name, defaultRemote, defaultAppId)
|
|
|
523
512
|
}
|
|
524
513
|
}
|
|
525
514
|
if (id == null) {
|
|
526
|
-
throw new
|
|
515
|
+
throw new AppsembleError(`App ID was not found in context.${name}.id nor in --app-id`);
|
|
527
516
|
}
|
|
528
|
-
return [id,
|
|
517
|
+
return [id, coerceRemote(resolvedRemote)];
|
|
529
518
|
}
|
|
530
|
-
exports.resolveAppIdAndRemote = resolveAppIdAndRemote;
|
|
531
519
|
//# sourceMappingURL=app.js.map
|