@swell/cli 1.0.2 → 2.0.1-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/LICENSE.md +21 -0
- package/README.md +2 -140
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +7 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +7 -0
- package/dist/app-command.d.ts +22 -0
- package/dist/app-command.js +45 -0
- package/dist/commands/app/_pull.d.ts +8 -0
- package/dist/commands/app/_pull.js +45 -0
- package/dist/commands/app/_uninstall.d.ts +12 -0
- package/dist/commands/app/_uninstall.js +45 -0
- package/dist/commands/app/info.d.ts +15 -0
- package/dist/commands/app/info.js +92 -0
- package/dist/commands/app/install.d.ts +17 -0
- package/dist/commands/app/install.js +177 -0
- package/dist/commands/app/push.d.ts +17 -0
- package/dist/commands/app/push.js +158 -0
- package/dist/commands/app/version.d.ts +18 -0
- package/dist/commands/app/version.js +156 -0
- package/dist/commands/app/watch.d.ts +15 -0
- package/dist/commands/app/watch.js +110 -0
- package/dist/commands/create/app.d.ts +21 -0
- package/dist/commands/create/app.js +210 -0
- package/dist/commands/create/content.d.ts +19 -0
- package/dist/commands/create/content.js +87 -0
- package/dist/commands/create/function.d.ts +25 -0
- package/dist/commands/create/function.js +191 -0
- package/dist/commands/create/index.d.ts +6 -0
- package/dist/commands/create/index.js +34 -0
- package/dist/commands/create/model.d.ts +19 -0
- package/dist/commands/create/model.js +87 -0
- package/dist/commands/create/notification.d.ts +25 -0
- package/dist/commands/create/notification.js +170 -0
- package/dist/commands/login.d.ts +22 -0
- package/dist/commands/login.js +77 -0
- package/dist/commands/logout.d.ts +8 -0
- package/dist/commands/logout.js +45 -0
- package/dist/commands/logs.d.ts +23 -0
- package/dist/commands/logs.js +207 -0
- package/dist/commands/switch.d.ts +10 -0
- package/dist/commands/switch.js +41 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +24 -0
- package/dist/create-collection-command.d.ts +26 -0
- package/dist/create-collection-command.js +97 -0
- package/dist/create-command.d.ts +16 -0
- package/dist/create-command.js +59 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/api.d.ts +13 -0
- package/dist/lib/api.js +117 -0
- package/dist/lib/app-config.d.ts +29 -0
- package/dist/lib/app-config.js +127 -0
- package/dist/lib/apps/app-config.d.ts +42 -0
- package/dist/lib/apps/app-config.js +167 -0
- package/dist/lib/apps/index.d.ts +67 -0
- package/dist/lib/apps/index.js +167 -0
- package/dist/lib/apps/paths.d.ts +18 -0
- package/dist/lib/apps/paths.js +54 -0
- package/dist/lib/bundle.d.ts +1 -0
- package/dist/lib/bundle.js +40 -0
- package/dist/lib/config.d.ts +37 -0
- package/dist/lib/config.js +131 -0
- package/dist/lib/constants.d.ts +5 -0
- package/dist/lib/constants.js +17 -0
- package/dist/lib/create/content.d.ts +23 -0
- package/dist/lib/create/content.js +40 -0
- package/dist/lib/create/function.d.ts +17 -0
- package/dist/lib/create/function.js +36 -0
- package/dist/lib/create/index.d.ts +13 -0
- package/dist/lib/create/index.js +20 -0
- package/dist/lib/create/model.d.ts +22 -0
- package/dist/lib/create/model.js +30 -0
- package/dist/lib/create/notification.d.ts +25 -0
- package/dist/lib/create/notification.js +1 -0
- package/dist/lib/info.d.ts +38 -0
- package/dist/lib/info.js +91 -0
- package/dist/lib/logs/index.d.ts +23 -0
- package/dist/lib/logs/index.js +88 -0
- package/dist/lib/logs/line-output.d.ts +5 -0
- package/dist/lib/logs/line-output.js +13 -0
- package/dist/lib/logs/output.d.ts +7 -0
- package/dist/lib/logs/output.js +8 -0
- package/dist/lib/logs/table-output.d.ts +11 -0
- package/dist/lib/logs/table-output.js +128 -0
- package/dist/lib/sessions.d.ts +9 -0
- package/dist/lib/sessions.js +110 -0
- package/dist/lib/stores.d.ts +63 -0
- package/dist/lib/stores.js +173 -0
- package/dist/lib/style.d.ts +24 -0
- package/dist/lib/style.js +29 -0
- package/dist/lib/swell-function-wrapper.d.ts +88 -0
- package/dist/lib/swell-function-wrapper.js +302 -0
- package/dist/remote-app-command.d.ts +36 -0
- package/dist/remote-app-command.js +367 -0
- package/dist/swell-command.d.ts +21 -0
- package/dist/swell-command.js +43 -0
- package/oclif.manifest.json +1085 -0
- package/package.json +81 -43
- package/LICENSE +0 -674
- package/dist/bin/multipart-parser-85ef9626.js +0 -456
- package/dist/bin/swell-0f71f07d.js +0 -30670
- package/dist/bin/swell.js +0 -24
- package/dist/man/swell.1 +0 -85
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import isEmpty from 'lodash/isEmpty.js';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { bundleFunction } from '../bundle.js';
|
|
6
|
+
import { ConfigType } from './index.js';
|
|
7
|
+
class IgnoringFileError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'IgnoringFileError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class FunctionProcessingError extends Error {
|
|
14
|
+
originalError;
|
|
15
|
+
constructor(message, error) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'FunctionProcessingError';
|
|
18
|
+
this.originalError = error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* An app configuration as defined by the Swell API. This implies that the
|
|
23
|
+
* app & the configuration are saved to the API.
|
|
24
|
+
*/
|
|
25
|
+
class AppConfig {
|
|
26
|
+
date_created;
|
|
27
|
+
file;
|
|
28
|
+
fileData;
|
|
29
|
+
id;
|
|
30
|
+
/** The local path of a remote AppConfig */
|
|
31
|
+
localPath;
|
|
32
|
+
name;
|
|
33
|
+
parent_id;
|
|
34
|
+
slug;
|
|
35
|
+
values = {};
|
|
36
|
+
constructor(attrs = {}) {
|
|
37
|
+
Object.assign(this, attrs);
|
|
38
|
+
const { localPath } = attrs;
|
|
39
|
+
if (localPath) {
|
|
40
|
+
this.fileData = fs.readFileSync(localPath, 'utf8');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
static create(attrs = {}) {
|
|
44
|
+
switch (attrs.type) {
|
|
45
|
+
case ConfigType.ASSET: {
|
|
46
|
+
return new AppConfigAsset(attrs);
|
|
47
|
+
}
|
|
48
|
+
case ConfigType.FUNCTION: {
|
|
49
|
+
return new AppConfigFunction(attrs);
|
|
50
|
+
}
|
|
51
|
+
case ConfigType.MODEL: {
|
|
52
|
+
return new AppConfigModel(attrs);
|
|
53
|
+
}
|
|
54
|
+
case ConfigType.NOTIFICATION: {
|
|
55
|
+
// the path can be a tpl file or a json file as we can use the `create`
|
|
56
|
+
// function when watching for changes. We need to make sure that we
|
|
57
|
+
// always use the json file for AppConfig.
|
|
58
|
+
attrs.localPath = AppConfigNotification.actualConfigPath(attrs.localPath);
|
|
59
|
+
return new AppConfigNotification(attrs);
|
|
60
|
+
}
|
|
61
|
+
default: {
|
|
62
|
+
// the default type is content so we want to make sure
|
|
63
|
+
// that we set the expected type
|
|
64
|
+
const defaultConfig = new AppConfigDefault(attrs);
|
|
65
|
+
defaultConfig.type = attrs.type;
|
|
66
|
+
return defaultConfig;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async postData() {
|
|
71
|
+
if (!this.localPath || !this.fileData) {
|
|
72
|
+
throw new Error('Missing local config path');
|
|
73
|
+
}
|
|
74
|
+
if (this.localPath.endsWith('.json')) {
|
|
75
|
+
try {
|
|
76
|
+
this.values = JSON.parse(this.fileData);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new IgnoringFileError('invalid JSON');
|
|
80
|
+
}
|
|
81
|
+
if (isEmpty(this.values)) {
|
|
82
|
+
throw new IgnoringFileError('empty JSON');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const postData = {
|
|
86
|
+
name: this.values?.name || this.name,
|
|
87
|
+
type: this.type,
|
|
88
|
+
values: this.values,
|
|
89
|
+
};
|
|
90
|
+
return this.preparePostData(postData);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
class AppConfigDefault extends AppConfig {
|
|
94
|
+
type = ConfigType.CONTENT;
|
|
95
|
+
preparePostData(postData) {
|
|
96
|
+
return postData;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
class AppConfigModel extends AppConfig {
|
|
100
|
+
type = ConfigType.MODEL;
|
|
101
|
+
preparePostData(postData) {
|
|
102
|
+
postData.values.collection = this.name;
|
|
103
|
+
return postData;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
class AppConfigNotification extends AppConfig {
|
|
107
|
+
type = ConfigType.NOTIFICATION;
|
|
108
|
+
/**
|
|
109
|
+
* The actual config path is the path to the JSON file that is used to
|
|
110
|
+
* configure the notification. This function ensures that the path is
|
|
111
|
+
* correct.
|
|
112
|
+
*
|
|
113
|
+
* @param localPath - json or tpl file path
|
|
114
|
+
* @returns - json file path
|
|
115
|
+
*/
|
|
116
|
+
static actualConfigPath(localPath) {
|
|
117
|
+
return localPath?.replace('.tpl', '.json');
|
|
118
|
+
}
|
|
119
|
+
preparePostData(postData) {
|
|
120
|
+
if (!this.localPath) {
|
|
121
|
+
throw new Error('Missing local config path');
|
|
122
|
+
}
|
|
123
|
+
const templateFile = this.localPath.replace('.json', '.tpl');
|
|
124
|
+
this.fileData = fs.readFileSync(templateFile, 'utf8');
|
|
125
|
+
if (this.fileData) {
|
|
126
|
+
postData.values.content = {
|
|
127
|
+
html: {
|
|
128
|
+
data: this.fileData.replaceAll('\r\n', '\n'),
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return postData;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
class AppConfigFunction extends AppConfig {
|
|
136
|
+
type = ConfigType.FUNCTION;
|
|
137
|
+
async preparePostData(postData) {
|
|
138
|
+
if (!this.localPath) {
|
|
139
|
+
throw new Error('Missing local config path');
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const { code, config } = await bundleFunction(this.localPath);
|
|
143
|
+
if (!config) {
|
|
144
|
+
throw new IgnoringFileError('Function must export a `config` object.');
|
|
145
|
+
}
|
|
146
|
+
const fileName = path.basename(this.localPath);
|
|
147
|
+
postData.file = {
|
|
148
|
+
data: code,
|
|
149
|
+
// we keep the file extension so we can easily identify the file path
|
|
150
|
+
// when we need to delete the function
|
|
151
|
+
ext: fileName.replace(this.name, ''),
|
|
152
|
+
};
|
|
153
|
+
postData.values = config;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
throw new FunctionProcessingError(`Unable to compile function ${this.name}`, error);
|
|
157
|
+
}
|
|
158
|
+
return postData;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
class AppConfigAsset extends AppConfig {
|
|
162
|
+
type = ConfigType.ASSET;
|
|
163
|
+
preparePostData(_postData) {
|
|
164
|
+
throw new Error('Assets not yet supported');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export { AppConfig, FunctionProcessingError, IgnoringFileError };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { AppConfig } from './app-config.js';
|
|
2
|
+
/**
|
|
3
|
+
* An app as defined by the Swell API. This is different than an app
|
|
4
|
+
* configuration that developers define.
|
|
5
|
+
*/
|
|
6
|
+
type App = {
|
|
7
|
+
client?: any;
|
|
8
|
+
client_id?: string;
|
|
9
|
+
configs?: AppConfig[];
|
|
10
|
+
date_created?: string;
|
|
11
|
+
date_updated?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
id?: string;
|
|
14
|
+
installed?: boolean;
|
|
15
|
+
name?: string;
|
|
16
|
+
private_id?: string;
|
|
17
|
+
public_id?: string;
|
|
18
|
+
published?: boolean;
|
|
19
|
+
version?: string;
|
|
20
|
+
};
|
|
21
|
+
type AppVersion = {
|
|
22
|
+
date_created: string;
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
notes: string;
|
|
26
|
+
version: string;
|
|
27
|
+
};
|
|
28
|
+
declare enum ConfigType {
|
|
29
|
+
ASSET = "asset",
|
|
30
|
+
CONTENT = "content",
|
|
31
|
+
FUNCTION = "function",
|
|
32
|
+
MODEL = "model",
|
|
33
|
+
NOTIFICATION = "notification",
|
|
34
|
+
SETTING = "setting",
|
|
35
|
+
WEBHOOK = "webhook"
|
|
36
|
+
}
|
|
37
|
+
type ConfigPathsType = {
|
|
38
|
+
[key in ConfigType]: string;
|
|
39
|
+
};
|
|
40
|
+
declare const ConfigPaths: ConfigPathsType;
|
|
41
|
+
declare enum ConfigInputFields {
|
|
42
|
+
FILE = "file",
|
|
43
|
+
NAME = "name",
|
|
44
|
+
TYPE = "type",
|
|
45
|
+
VALUES = "values",
|
|
46
|
+
VERSION = "version"
|
|
47
|
+
}
|
|
48
|
+
declare function hasAllowedFileExtension(file: string, configType?: ConfigType): boolean;
|
|
49
|
+
declare function getConfigTypeFromPath(path: string): ConfigType | undefined;
|
|
50
|
+
declare function getConfigTypeKeyFromValue(value: string): string | undefined;
|
|
51
|
+
declare function fileExists(filePath: string): boolean;
|
|
52
|
+
declare function writeFile(file: string, data: string): Promise<void>;
|
|
53
|
+
declare function writeJsonFile(file: string, data?: any): Promise<void>;
|
|
54
|
+
declare function appLogoIcon(appPath: string): string | undefined;
|
|
55
|
+
declare function isAppLogoIcon(file: string): boolean;
|
|
56
|
+
declare function appConfigFromFile(configFile: string, configType: ConfigType): AppConfig;
|
|
57
|
+
declare function findAppConfig(app: App, file: string, configType: ConfigType): AppConfig | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Get files to push in batches prioritized by dependencies
|
|
60
|
+
*
|
|
61
|
+
* @param appConfigs - List of app configs
|
|
62
|
+
* @returns {Array} Batches of files to push
|
|
63
|
+
*/
|
|
64
|
+
declare function batchAppConfigsByType(appConfigs: AppConfig[]): Array<any>;
|
|
65
|
+
export { App, AppVersion, ConfigInputFields, ConfigPaths, ConfigPathsType, ConfigType, appConfigFromFile, appLogoIcon, batchAppConfigsByType, fileExists, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, hasAllowedFileExtension, isAppLogoIcon, writeFile, writeJsonFile, };
|
|
66
|
+
export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
|
|
67
|
+
export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, configFilePath, } from './paths.js';
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { pluralize, titleize } from 'inflection';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { appConfigFileExists } from '../app-config.js';
|
|
5
|
+
import { AppConfig } from './app-config.js';
|
|
6
|
+
var ConfigType;
|
|
7
|
+
(function (ConfigType) {
|
|
8
|
+
ConfigType["ASSET"] = "asset";
|
|
9
|
+
ConfigType["CONTENT"] = "content";
|
|
10
|
+
ConfigType["FUNCTION"] = "function";
|
|
11
|
+
ConfigType["MODEL"] = "model";
|
|
12
|
+
ConfigType["NOTIFICATION"] = "notification";
|
|
13
|
+
ConfigType["SETTING"] = "setting";
|
|
14
|
+
ConfigType["WEBHOOK"] = "webhook";
|
|
15
|
+
})(ConfigType || (ConfigType = {}));
|
|
16
|
+
// Models and content should come first in order
|
|
17
|
+
const ConfigTypeBatchOrder = [
|
|
18
|
+
ConfigType.MODEL,
|
|
19
|
+
ConfigType.CONTENT,
|
|
20
|
+
ConfigType.ASSET,
|
|
21
|
+
ConfigType.FUNCTION,
|
|
22
|
+
ConfigType.NOTIFICATION,
|
|
23
|
+
ConfigType.SETTING,
|
|
24
|
+
ConfigType.WEBHOOK,
|
|
25
|
+
];
|
|
26
|
+
const ConfigPaths = {
|
|
27
|
+
...Object.fromEntries(Object.keys(ConfigType).map((type) => [
|
|
28
|
+
type,
|
|
29
|
+
// a bit of a hack to make the path singular for content configs
|
|
30
|
+
// need to refactor this to a function if we add more exceptions
|
|
31
|
+
type === 'CONTENT' ? 'content' : pluralize(type.toLowerCase()),
|
|
32
|
+
])),
|
|
33
|
+
};
|
|
34
|
+
var ConfigInputFields;
|
|
35
|
+
(function (ConfigInputFields) {
|
|
36
|
+
ConfigInputFields["FILE"] = "file";
|
|
37
|
+
ConfigInputFields["NAME"] = "name";
|
|
38
|
+
ConfigInputFields["TYPE"] = "type";
|
|
39
|
+
ConfigInputFields["VALUES"] = "values";
|
|
40
|
+
ConfigInputFields["VERSION"] = "version";
|
|
41
|
+
})(ConfigInputFields || (ConfigInputFields = {}));
|
|
42
|
+
function hasAllowedFileExtension(file, configType) {
|
|
43
|
+
let allowedFileExtensions = new Set([
|
|
44
|
+
'.js',
|
|
45
|
+
'.json',
|
|
46
|
+
'.tpl',
|
|
47
|
+
'.ts',
|
|
48
|
+
]);
|
|
49
|
+
if (configType === ConfigType.NOTIFICATION) {
|
|
50
|
+
allowedFileExtensions = new Set(['.json']);
|
|
51
|
+
}
|
|
52
|
+
return allowedFileExtensions.has(path.parse(file).ext);
|
|
53
|
+
}
|
|
54
|
+
function getConfigTypeFromPath(path) {
|
|
55
|
+
for (const type in ConfigPaths) {
|
|
56
|
+
if (ConfigPaths[type] === path) {
|
|
57
|
+
const configType = ConfigType[type];
|
|
58
|
+
return configType;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
function getConfigTypeKeyFromValue(value) {
|
|
64
|
+
return Object.keys(ConfigType).find((key) => ConfigType[key] === value);
|
|
65
|
+
}
|
|
66
|
+
function fileExists(filePath) {
|
|
67
|
+
try {
|
|
68
|
+
// eslint-disable-next-line no-bitwise
|
|
69
|
+
fs.accessSync(filePath, fs.constants.R_OK | fs.constants.W_OK);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error.code !== 'ENOENT') {
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function writeFile(file, data) {
|
|
80
|
+
return new Promise((resolve, _reject) => {
|
|
81
|
+
fs.mkdir(path.dirname(file), { recursive: true }, (error) => {
|
|
82
|
+
if (error) {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
fs.writeFile(file, data, (error) => {
|
|
86
|
+
if (error) {
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
resolve();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function writeJsonFile(file, data = {}) {
|
|
95
|
+
return writeFile(file, `${JSON.stringify(data, null, 2)}\n`);
|
|
96
|
+
}
|
|
97
|
+
function appLogoIcon(appPath) {
|
|
98
|
+
const assetsDirPath = path.join(appPath, ConfigPaths['ASSET']);
|
|
99
|
+
if (!appConfigFileExists(assetsDirPath)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
for (const configFile of fs.readdirSync(assetsDirPath)) {
|
|
103
|
+
if (isAppLogoIcon(configFile)) {
|
|
104
|
+
return path.join(assetsDirPath, configFile);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function isAppLogoIcon(file) {
|
|
109
|
+
return path.parse(file).name === 'icon';
|
|
110
|
+
}
|
|
111
|
+
function appConfigFromFile(configFile, configType) {
|
|
112
|
+
const { name } = path.parse(configFile);
|
|
113
|
+
const appConfig = AppConfig.create({
|
|
114
|
+
localPath: configFile,
|
|
115
|
+
name,
|
|
116
|
+
type: configType,
|
|
117
|
+
values: {},
|
|
118
|
+
});
|
|
119
|
+
return appConfig;
|
|
120
|
+
}
|
|
121
|
+
function findAppConfig(app, file, configType) {
|
|
122
|
+
const { name } = path.parse(file);
|
|
123
|
+
const config = app?.configs?.find((c) => c && c.type === configType && c.name === name);
|
|
124
|
+
return config;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get files to push in batches prioritized by dependencies
|
|
128
|
+
*
|
|
129
|
+
* @param appConfigs - List of app configs
|
|
130
|
+
* @returns {Array} Batches of files to push
|
|
131
|
+
*/
|
|
132
|
+
function batchAppConfigsByType(appConfigs) {
|
|
133
|
+
const modelsWithExtends = appConfigs.filter((item) => item.type === 'model' && item.values.extends);
|
|
134
|
+
const modelsWithoutExtends = appConfigs.filter((item) => item.type === 'model' && !item.values.extends);
|
|
135
|
+
const contentTypesWithCollection = appConfigs.filter((item) => item.type === 'content' && item.values.collection);
|
|
136
|
+
const contentTypesWithoutCollection = appConfigs.filter((item) => item.type === 'content' && !item.values.collection);
|
|
137
|
+
const prioritized = [
|
|
138
|
+
...modelsWithExtends,
|
|
139
|
+
...modelsWithoutExtends,
|
|
140
|
+
...contentTypesWithCollection,
|
|
141
|
+
...contentTypesWithoutCollection,
|
|
142
|
+
];
|
|
143
|
+
const sorted = [
|
|
144
|
+
...prioritized,
|
|
145
|
+
...appConfigs.filter((item) => !prioritized.includes(item)),
|
|
146
|
+
];
|
|
147
|
+
const batches = [];
|
|
148
|
+
// eslint-disable-next-line guard-for-in
|
|
149
|
+
for (const key in ConfigTypeBatchOrder) {
|
|
150
|
+
const configType = ConfigTypeBatchOrder[key];
|
|
151
|
+
const configsByType = sorted.filter((item) => item.type === configType);
|
|
152
|
+
if (configsByType.length === 0) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
batches.push({
|
|
156
|
+
configs: configsByType,
|
|
157
|
+
label: titleize(configType),
|
|
158
|
+
type: configType,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return batches;
|
|
162
|
+
}
|
|
163
|
+
export { ConfigInputFields, ConfigPaths, ConfigType,
|
|
164
|
+
// functions
|
|
165
|
+
appConfigFromFile, appLogoIcon, batchAppConfigsByType, fileExists, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, hasAllowedFileExtension, isAppLogoIcon, writeFile, writeJsonFile, };
|
|
166
|
+
export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
|
|
167
|
+
export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, configFilePath, } from './paths.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AppConfig, ConfigType } from './index.js';
|
|
2
|
+
declare function allConfigDirsPaths(appPath: string): Generator<{
|
|
3
|
+
configDirPath: string;
|
|
4
|
+
configType: ConfigType;
|
|
5
|
+
}>;
|
|
6
|
+
declare function allConfigFilesPaths(appPath: string): Generator<{
|
|
7
|
+
configDirPath: string;
|
|
8
|
+
configFile: string;
|
|
9
|
+
configType: ConfigType;
|
|
10
|
+
}, void, unknown>;
|
|
11
|
+
declare function allConfigFilesPathsByType(appPath: string, configTypeKey: string): Generator<{
|
|
12
|
+
configDirPath: string;
|
|
13
|
+
configFile: string;
|
|
14
|
+
configType: ConfigType;
|
|
15
|
+
}, void, unknown>;
|
|
16
|
+
declare function configFilePath(appPath: string, config: AppConfig): string;
|
|
17
|
+
declare function getAllConfigPaths(): Array<string>;
|
|
18
|
+
export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, configFilePath, };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { appConfigFileExists } from '../app-config.js';
|
|
4
|
+
import { ConfigPaths, ConfigType, hasAllowedFileExtension, } from './index.js';
|
|
5
|
+
function* allConfigDirsPaths(appPath) {
|
|
6
|
+
for (const type in ConfigType) {
|
|
7
|
+
if (Object.prototype.hasOwnProperty.call(ConfigType, type)) {
|
|
8
|
+
const configType = ConfigType[type];
|
|
9
|
+
const configPath = ConfigPaths[type];
|
|
10
|
+
const configDirPath = path.join(appPath, configPath);
|
|
11
|
+
if (appConfigFileExists(configDirPath)) {
|
|
12
|
+
yield { configDirPath, configType };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function* allConfigFilesPaths(appPath) {
|
|
18
|
+
for (const { configDirPath, configType } of allConfigDirsPaths(appPath)) {
|
|
19
|
+
yield* allConfigFilesInDir(configDirPath, configType);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function* allConfigFilesPathsByType(appPath, configTypeKey) {
|
|
23
|
+
const configPath = ConfigPaths[configTypeKey];
|
|
24
|
+
const configDirPath = path.join(appPath, configPath);
|
|
25
|
+
yield* allConfigFilesInDir(configDirPath, ConfigType[configTypeKey]);
|
|
26
|
+
}
|
|
27
|
+
function* allConfigFilesInDir(configDirPath, configType) {
|
|
28
|
+
for (const file of fs.readdirSync(configDirPath)) {
|
|
29
|
+
const configFile = path.join(configDirPath, file);
|
|
30
|
+
// ignore sub-directories, e.g. functions/lib
|
|
31
|
+
// ignore files that don't have the allowed extensions
|
|
32
|
+
if (fs.lstatSync(configFile).isDirectory() ||
|
|
33
|
+
!hasAllowedFileExtension(configFile, configType)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
yield { configDirPath, configFile, configType };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function configFilePath(appPath, config) {
|
|
40
|
+
const configPath = ConfigPaths[config.type.toLocaleUpperCase()];
|
|
41
|
+
const fileExt = config.file?.ext || '.json';
|
|
42
|
+
return path.join(appPath, configPath, [config.name, fileExt].join(''));
|
|
43
|
+
}
|
|
44
|
+
function getAllConfigPaths() {
|
|
45
|
+
const configPaths = [];
|
|
46
|
+
for (const type in ConfigPaths) {
|
|
47
|
+
if (Object.prototype.hasOwnProperty.call(ConfigPaths, type)) {
|
|
48
|
+
const configPath = ConfigPaths[type];
|
|
49
|
+
configPaths.push(configPath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return configPaths;
|
|
53
|
+
}
|
|
54
|
+
export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, configFilePath, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function bundleFunction(filePath: string): Promise<any>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
export async function bundleFunction(filePath) {
|
|
6
|
+
try {
|
|
7
|
+
const buildResult = await esbuild.build({
|
|
8
|
+
bundle: true,
|
|
9
|
+
entryPoints: [filePath],
|
|
10
|
+
format: 'cjs',
|
|
11
|
+
metafile: true,
|
|
12
|
+
platform: 'node',
|
|
13
|
+
target: ['chrome58'],
|
|
14
|
+
write: false, // Do not write the output to a file
|
|
15
|
+
});
|
|
16
|
+
const { metafile: { inputs }, outputFiles: [{ text: origCode }], } = buildResult;
|
|
17
|
+
// TODO: make a separate method to get dependencies for watch command
|
|
18
|
+
const dependencies = Object.keys(inputs).filter((input) => !input.startsWith('node_modules/'));
|
|
19
|
+
let code = origCode
|
|
20
|
+
.split('\n')
|
|
21
|
+
.map((line) => line.trim().startsWith('module.exports')
|
|
22
|
+
? line.replace('module.exports', 'var moduleExports')
|
|
23
|
+
: line)
|
|
24
|
+
.join('\n');
|
|
25
|
+
// eslint-disable-next-line no-new-func
|
|
26
|
+
const evalFn = new Function(`${code}\nreturn { ...moduleExports }`);
|
|
27
|
+
const { config, ...moduleExports } = evalFn();
|
|
28
|
+
code += getSwellFunctionWrapper();
|
|
29
|
+
return { code, config, dependencies, moduleExports };
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.log(error);
|
|
33
|
+
throw new Error(`Unable to compile function ${filePath}: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function getSwellFunctionWrapper() {
|
|
37
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
38
|
+
const __dirname = path.dirname(__filename);
|
|
39
|
+
return fs.readFileSync(path.join(__dirname, './swell-function-wrapper.js'), 'utf8');
|
|
40
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
declare const env: any;
|
|
2
|
+
declare function ensureLoggedIn(storeId: string): Promise<any>;
|
|
3
|
+
declare function get(key: string): any;
|
|
4
|
+
declare function getAll(): any;
|
|
5
|
+
declare function getDefaultStore(): any;
|
|
6
|
+
declare function setDefaultStore(storeId: string): void;
|
|
7
|
+
declare function getSessionId(storeId: string): any;
|
|
8
|
+
/**
|
|
9
|
+
* Set store data for later access.
|
|
10
|
+
*
|
|
11
|
+
* It replaces the previous store value if it exists and only stores the
|
|
12
|
+
* following attributes:
|
|
13
|
+
*
|
|
14
|
+
* - sessionId
|
|
15
|
+
* - storeId
|
|
16
|
+
* - name
|
|
17
|
+
*
|
|
18
|
+
* @param store - The store to set
|
|
19
|
+
* @returns {void}
|
|
20
|
+
*/
|
|
21
|
+
declare function setStore(store: Store): void;
|
|
22
|
+
declare function getUser(): any;
|
|
23
|
+
declare function setUser(user: User): void;
|
|
24
|
+
declare function clear(): void;
|
|
25
|
+
declare const config: {
|
|
26
|
+
clear: typeof clear;
|
|
27
|
+
ensureLoggedIn: typeof ensureLoggedIn;
|
|
28
|
+
get: typeof get;
|
|
29
|
+
getAll: typeof getAll;
|
|
30
|
+
getDefaultStore: typeof getDefaultStore;
|
|
31
|
+
getSessionId: typeof getSessionId;
|
|
32
|
+
getUser: typeof getUser;
|
|
33
|
+
setDefaultStore: typeof setDefaultStore;
|
|
34
|
+
setStore: typeof setStore;
|
|
35
|
+
setUser: typeof setUser;
|
|
36
|
+
};
|
|
37
|
+
export { config as default, env };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
import Configstore from 'configstore';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import style from './style.js';
|
|
6
|
+
const configPath = process.env.NODE_ENV === 'test'
|
|
7
|
+
? path.resolve(process.cwd(), 'test', '.swell', 'config.json')
|
|
8
|
+
: path.resolve(homedir(), '.swell', 'config.json');
|
|
9
|
+
const configDir = process.env.NODE_ENV === 'test'
|
|
10
|
+
? path.resolve(process.cwd(), 'test', 'fixtures')
|
|
11
|
+
: homedir();
|
|
12
|
+
const configStore = new Configstore('swell', {
|
|
13
|
+
// user: {...},
|
|
14
|
+
// defaultStore: '<store_id>',
|
|
15
|
+
// stores: [
|
|
16
|
+
// {
|
|
17
|
+
// storeId: '<store_id>',
|
|
18
|
+
// sessionId: '<session_id>',
|
|
19
|
+
// name: '<store_name>',
|
|
20
|
+
// }
|
|
21
|
+
// ]
|
|
22
|
+
}, {
|
|
23
|
+
configPath,
|
|
24
|
+
});
|
|
25
|
+
const env = new Conf({
|
|
26
|
+
configName: 'env',
|
|
27
|
+
cwd: path.join(configDir, '.swell'),
|
|
28
|
+
projectName: 'swell',
|
|
29
|
+
projectSuffix: '',
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* Get specified attributes for given object.
|
|
33
|
+
*
|
|
34
|
+
* @param object - The object to get attributes from
|
|
35
|
+
* @param attributes - The attributes to get from the
|
|
36
|
+
*
|
|
37
|
+
* @returns An array of the values of the specified attributes
|
|
38
|
+
*/
|
|
39
|
+
function selectAttributes(object, attributes) {
|
|
40
|
+
// eslint-disable-next-line unicorn/no-array-reduce
|
|
41
|
+
return attributes.reduce((acc, key) => {
|
|
42
|
+
acc[key] = object[key];
|
|
43
|
+
return acc;
|
|
44
|
+
}, {});
|
|
45
|
+
}
|
|
46
|
+
async function ensureLoggedIn(storeId) {
|
|
47
|
+
const currentStore = storeId || getDefaultStore();
|
|
48
|
+
const sessionId = getSessionId(currentStore);
|
|
49
|
+
// still not logged in
|
|
50
|
+
if (!sessionId) {
|
|
51
|
+
const message = [
|
|
52
|
+
`You are not logged in to store ${style.storeId(currentStore)}.`,
|
|
53
|
+
`Use ${style.command('swell login')} to authenticate.`,
|
|
54
|
+
].join(' ');
|
|
55
|
+
throw new Error(message);
|
|
56
|
+
}
|
|
57
|
+
return sessionId;
|
|
58
|
+
}
|
|
59
|
+
function get(key) {
|
|
60
|
+
return configStore.get(key);
|
|
61
|
+
}
|
|
62
|
+
function getAll() {
|
|
63
|
+
return configStore.all;
|
|
64
|
+
}
|
|
65
|
+
function getDefaultStore() {
|
|
66
|
+
return configStore.get('defaultStore');
|
|
67
|
+
}
|
|
68
|
+
function setDefaultStore(storeId) {
|
|
69
|
+
return configStore.set('defaultStore', storeId);
|
|
70
|
+
}
|
|
71
|
+
function getSessionId(storeId) {
|
|
72
|
+
if (!configStore.has('stores'))
|
|
73
|
+
return;
|
|
74
|
+
const store = configStore
|
|
75
|
+
.get('stores')
|
|
76
|
+
.find((store) => store.storeId === storeId);
|
|
77
|
+
return store?.sessionId;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Set store data for later access.
|
|
81
|
+
*
|
|
82
|
+
* It replaces the previous store value if it exists and only stores the
|
|
83
|
+
* following attributes:
|
|
84
|
+
*
|
|
85
|
+
* - sessionId
|
|
86
|
+
* - storeId
|
|
87
|
+
* - name
|
|
88
|
+
*
|
|
89
|
+
* @param store - The store to set
|
|
90
|
+
* @returns {void}
|
|
91
|
+
*/
|
|
92
|
+
function setStore(store) {
|
|
93
|
+
const stores = configStore.get('stores') || [];
|
|
94
|
+
const storeIndex = stores.findIndex((s) => s.storeId === store.storeId);
|
|
95
|
+
const storeConfig = selectAttributes(store, [
|
|
96
|
+
'storeId',
|
|
97
|
+
'sessionId',
|
|
98
|
+
'name',
|
|
99
|
+
'email',
|
|
100
|
+
]);
|
|
101
|
+
if (storeIndex === -1) {
|
|
102
|
+
stores.push(storeConfig);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
stores[storeIndex] = storeConfig;
|
|
106
|
+
}
|
|
107
|
+
return configStore.set('stores', stores);
|
|
108
|
+
}
|
|
109
|
+
function getUser() {
|
|
110
|
+
return configStore.get('user');
|
|
111
|
+
}
|
|
112
|
+
function setUser(user) {
|
|
113
|
+
const userConfig = selectAttributes(user, ['name', 'email']);
|
|
114
|
+
return configStore.set('user', userConfig);
|
|
115
|
+
}
|
|
116
|
+
function clear() {
|
|
117
|
+
return configStore.clear();
|
|
118
|
+
}
|
|
119
|
+
const config = {
|
|
120
|
+
clear,
|
|
121
|
+
ensureLoggedIn,
|
|
122
|
+
get,
|
|
123
|
+
getAll,
|
|
124
|
+
getDefaultStore,
|
|
125
|
+
getSessionId,
|
|
126
|
+
getUser,
|
|
127
|
+
setDefaultStore,
|
|
128
|
+
setStore,
|
|
129
|
+
setUser,
|
|
130
|
+
};
|
|
131
|
+
export { config as default, env };
|