@ixon-cdk/core 1.0.0-alpha.9 → 1.1.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,31 @@
1
- const fs = require("fs");
2
- const axios = require("axios");
3
- const ApiBaseService = require("./base.service");
1
+ const fs = require('fs');
2
+ const ApiBaseService = require('./base.service');
3
+ const httpRequest = require('../http-request');
4
4
 
5
5
  module.exports = class AuthService extends ApiBaseService {
6
6
  logIn(credentials) {
7
7
  const c = credentials;
8
- return axios.post(
9
- `${this._getApiBaseUrl()}/access-tokens`,
10
- { expiresIn: 86400 },
11
- {
12
- headers: {
13
- ...this._getApiDefaultHeaders(),
14
- "User-Agent": "ComponentDevKit",
15
- Authorization: `Basic ${Buffer.from(
16
- c.email + ":" + (c.otp || "") + ":" + c.password,
17
- "utf-8"
18
- ).toString("base64")}`,
19
- },
20
- }
21
- );
8
+ return httpRequest({
9
+ method: 'POST',
10
+ url: `${this._getApiBaseUrl()}/access-tokens`,
11
+ data: { expiresIn: 86400 },
12
+ headers: {
13
+ ...this._getApiDefaultHeaders(),
14
+ 'User-Agent': 'ComponentDevKit',
15
+ Authorization: `Basic ${Buffer.from(
16
+ `${c.email}:${c.otp || ''}:${c.password}`,
17
+ 'utf-8',
18
+ ).toString('base64')}`,
19
+ },
20
+ });
22
21
  }
23
22
 
24
23
  logOut() {
25
24
  const secretId = this._getSecretId();
26
25
  if (secretId) {
27
- return axios.delete(`${this._getApiBaseUrl()}/access-tokens/me`, {
26
+ return httpRequest({
27
+ method: 'DELETE',
28
+ url: `${this._getApiBaseUrl()}/access-tokens/me`,
28
29
  headers: {
29
30
  ...this._getApiDefaultHeaders(),
30
31
  Authorization: this._getApiAuthHeaderValue(),
@@ -40,8 +41,8 @@ module.exports = class AuthService extends ApiBaseService {
40
41
 
41
42
  remember(secretId) {
42
43
  fs.writeFileSync(this._getAccessTokenFilePath(), secretId, {
43
- encoding: "utf8",
44
- flag: "w",
44
+ encoding: 'utf8',
45
+ flag: 'w',
45
46
  });
46
47
  }
47
48
 
@@ -1,12 +1,12 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const ConfigService = require("../config/config.service");
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const ConfigService = require('../config/config.service');
4
4
 
5
5
  module.exports = class ApiBaseService {
6
6
  _configSrv = new ConfigService();
7
7
 
8
8
  _getAccessTokenFilePath() {
9
- return path.join(require("../utils").getRootDir(), ".accesstoken");
9
+ return path.join(require('../utils').getRootDir(), '.accesstoken');
10
10
  }
11
11
 
12
12
  _getApiAuthHeaderValue() {
@@ -19,9 +19,9 @@ module.exports = class ApiBaseService {
19
19
 
20
20
  _getApiDefaultHeaders() {
21
21
  return {
22
- "Content-Type": "application/json",
23
- "Api-Application": this._configSrv.getApiApplication(),
24
- "Api-Version": this._configSrv.getApiVersion(),
22
+ 'Content-Type': 'application/json',
23
+ 'Api-Application': this._configSrv.getApiApplication(),
24
+ 'Api-Version': this._configSrv.getApiVersion(),
25
25
  };
26
26
  }
27
27
 
@@ -29,7 +29,7 @@ module.exports = class ApiBaseService {
29
29
  let secretId;
30
30
  try {
31
31
  secretId = fs.readFileSync(this._getAccessTokenFilePath(), {
32
- encoding: "utf-8",
32
+ encoding: 'utf-8',
33
33
  });
34
34
  } catch {
35
35
  // do nothing
@@ -1,38 +1,32 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const axios = require("axios");
4
- const ApiBaseService = require("./base.service");
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const ApiBaseService = require('./base.service');
4
+ const httpRequest = require('../http-request');
5
5
 
6
6
  module.exports = class DeployService extends ApiBaseService {
7
7
  deploy(companyId, templateId, file) {
8
8
  const url = `${this._getApiBaseUrl()}/page-component-templates/${templateId}/version-upload`;
9
- const body = fs.readFileSync(
10
- path.join(require("../utils").getRootDir(), file)
11
- );
9
+ const body = fs.readFileSync(path.join(require('../utils').getRootDir(), file));
12
10
  const headers = {
13
11
  ...this._getApiDefaultHeaders(),
14
- "Content-Type": "application/x-www-form-urlencoded",
15
- "Api-Company": companyId,
12
+ 'Content-Type': 'application/x-www-form-urlencoded',
13
+ 'Api-Company': companyId,
16
14
  Authorization: this._getApiAuthHeaderValue(),
17
15
  };
18
- return axios.post(url, body, { headers }).then((res) => res.data.data);
16
+ return httpRequest({ method: 'POST', url, data: body, headers }).then((res) => res.data.data);
19
17
  }
20
18
 
21
19
  deployAndPublish(companyId, templateId, file) {
22
20
  return this.deploy(companyId, templateId, file).then((data) =>
23
- this._publish(companyId, data.publicId)
21
+ this._publish(companyId, data.publicId),
24
22
  );
25
23
  }
26
24
 
27
25
  fetchPublishable(companyId, templateId) {
28
- return this._fetchDescTemplateVersions(companyId, templateId).then(
29
- (versions) => {
30
- const publishedIdx = versions.findIndex(
31
- (v) => typeof v.publishedOn === "string"
32
- );
33
- return publishedIdx !== -1 ? versions.slice(0, publishedIdx) : versions;
34
- }
35
- );
26
+ return this._fetchDescTemplateVersions(companyId, templateId).then((versions) => {
27
+ const publishedIdx = versions.findIndex((v) => typeof v.publishedOn === 'string');
28
+ return publishedIdx !== -1 ? versions.slice(0, publishedIdx) : versions;
29
+ });
36
30
  }
37
31
 
38
32
  generatePreviewLink(companyId, versionId) {
@@ -40,32 +34,32 @@ module.exports = class DeployService extends ApiBaseService {
40
34
  this._fetchBrandingFqdn(companyId),
41
35
  this._fetchTemplateVersion(companyId, versionId),
42
36
  ])
43
- .then(([fqdn, version]) => {
44
- return this._fetchTemplate(companyId, version.template.publicId).then(
45
- (template) => [fqdn, version, template]
46
- );
47
- })
37
+ .then(([fqdn, version]) =>
38
+ this._fetchTemplate(companyId, version.template.publicId).then((template) => [
39
+ fqdn,
40
+ version,
41
+ template,
42
+ ]),
43
+ )
48
44
  .then(([fqdn, version, template]) => {
49
- const hash = require("../utils").generatePreviewHash(
45
+ const hash = require('../utils').generatePreviewHash(
50
46
  template.publicId,
51
47
  template.name,
52
48
  version.publicId,
53
49
  version.number,
54
- version.mainPath
50
+ version.mainPath,
55
51
  );
56
52
  const search = `?pctpvw=${encodeURIComponent(hash)}`;
57
- return `https://${fqdn || "[YOUR-PLATFORM-DOMAIN]"}/portal/${search}`;
53
+ return `https://${fqdn || '[YOUR-PLATFORM-DOMAIN]'}/portal/${search}`;
58
54
  });
59
55
  }
60
56
 
61
57
  publishLatestVersion(companyId, templateId) {
62
58
  return this.fetchPublishable(companyId, templateId).then((data) => {
63
59
  if (!data.length) {
64
- return Promise.reject("Template has no publishable versions");
60
+ return Promise.reject('Template has no publishable versions');
65
61
  }
66
- const latest = data.sort(
67
- (v1, v2) => Number(v2.number) - Number(v1.number)
68
- )[0];
62
+ const latest = data.sort((v1, v2) => Number(v2.number) - Number(v1.number))[0];
69
63
  return this._publish(companyId, latest.publicId);
70
64
  });
71
65
  }
@@ -73,26 +67,26 @@ module.exports = class DeployService extends ApiBaseService {
73
67
  publishVersion(companyId, templateId, versionNumber) {
74
68
  return this.fetchPublishable(companyId, templateId).then((data) => {
75
69
  if (!data.length) {
76
- return Promise.reject("Template has no publishable versions");
70
+ return Promise.reject('Template has no publishable versions');
77
71
  }
78
72
  const _version = data.find((v) => v.number === String(versionNumber));
79
- if (!!_version) {
73
+ if (_version) {
80
74
  return this._publish(companyId, _version.publicId);
81
75
  }
82
- return Promise.reject("Could not match!");
76
+ return Promise.reject('Could not match!');
83
77
  });
84
78
  }
85
79
 
86
80
  _fetchBrandingFqdn(companyId) {
87
81
  const url = `${this._getApiBaseUrl()}/companies/me`;
88
- const params = { fields: "branding(fqdn)" };
82
+ const params = { fields: 'branding(fqdn)' };
89
83
  const headers = {
90
84
  ...this._getApiDefaultHeaders(),
91
- "Api-Company": companyId,
85
+ 'Api-Company': companyId,
92
86
  Authorization: this._getApiAuthHeaderValue(),
93
87
  };
94
88
  // Lookup company FQDN (premium branding)
95
- return axios.get(url, { params, headers }).then((res) => {
89
+ return httpRequest({ method: 'GET', url, params, headers }).then((res) => {
96
90
  const myCompany = res.data.data;
97
91
  if (myCompany.branding && myCompany.branding.fqdn) {
98
92
  return myCompany.branding.fqdn;
@@ -100,7 +94,7 @@ module.exports = class DeployService extends ApiBaseService {
100
94
  // Or fallback to sector FQDN
101
95
  const _url = `${this._getApiBaseUrl()}/sectors/me`;
102
96
  const _headers = { ...this._getApiDefaultHeaders() };
103
- return axios.get(_url, { params, headers: _headers }).then((res) => {
97
+ return httpRequest({ method: 'GET', url: _url, params, headers: _headers }).then((res) => {
104
98
  const mySector = res.data.data;
105
99
  if (mySector.branding && mySector.branding.fqdn) {
106
100
  return mySector.branding.fqdn;
@@ -113,42 +107,40 @@ module.exports = class DeployService extends ApiBaseService {
113
107
  _fetchDescTemplateVersions(companyId, templateId) {
114
108
  const url = `${this._getApiBaseUrl()}/page-component-template-versions`;
115
109
  const params = {
116
- fields: "*,template(*)",
110
+ fields: '*,template(*)',
117
111
  filters: `eq(template.publicId,"${templateId}")`,
118
- "page-size": "100",
112
+ 'page-size': '100',
119
113
  };
120
114
  const headers = {
121
115
  ...this._getApiDefaultHeaders(),
122
- "Api-Company": companyId,
116
+ 'Api-Company': companyId,
123
117
  Authorization: this._getApiAuthHeaderValue(),
124
118
  };
125
- return axios.get(url, { params, headers }).then((res) => {
126
- return res.data.data.sort(
127
- (v1, v2) => Number(v2.number) - Number(v1.number)
128
- );
129
- });
119
+ return httpRequest({ method: 'GET', url, params, headers }).then((res) =>
120
+ res.data.data.sort((v1, v2) => Number(v2.number) - Number(v1.number)),
121
+ );
130
122
  }
131
123
 
132
124
  _fetchTemplate(companyId, templateId) {
133
125
  const url = `${this._getApiBaseUrl()}/page-component-templates/${templateId}`;
134
- const params = { fields: "*" };
126
+ const params = { fields: '*' };
135
127
  const headers = {
136
128
  ...this._getApiDefaultHeaders(),
137
- "Api-Company": companyId,
129
+ 'Api-Company': companyId,
138
130
  Authorization: this._getApiAuthHeaderValue(),
139
131
  };
140
- return axios.get(url, { params, headers }).then((res) => res.data.data);
132
+ return httpRequest({ method: 'GET', url, params, headers }).then((res) => res.data.data);
141
133
  }
142
134
 
143
135
  _fetchTemplateVersion(companyId, versionId) {
144
136
  const url = `${this._getApiBaseUrl()}/page-component-template-versions/${versionId}`;
145
- const params = { fields: "*,template(*)" };
137
+ const params = { fields: '*,template(*)' };
146
138
  const headers = {
147
139
  ...this._getApiDefaultHeaders(),
148
- "Api-Company": companyId,
140
+ 'Api-Company': companyId,
149
141
  Authorization: this._getApiAuthHeaderValue(),
150
142
  };
151
- return axios.get(url, { params, headers }).then((res) => res.data.data);
143
+ return httpRequest({ method: 'GET', url, params, headers }).then((res) => res.data.data);
152
144
  }
153
145
 
154
146
  _publish(companyId, versionId) {
@@ -156,9 +148,9 @@ module.exports = class DeployService extends ApiBaseService {
156
148
  const body = { published: true };
157
149
  const headers = {
158
150
  ...this._getApiDefaultHeaders(),
159
- "Api-Company": companyId,
151
+ 'Api-Company': companyId,
160
152
  Authorization: this._getApiAuthHeaderValue(),
161
153
  };
162
- return axios.patch(url, body, { headers });
154
+ return httpRequest({ method: 'PATCH', url, data: body, headers });
163
155
  }
164
156
  };
@@ -1,25 +1,25 @@
1
- const fs = require("fs");
2
- const { merge } = require("lodash");
3
- const path = require("path");
4
- const { getRootDir, logFileCrudMessage, logErrorMessage } = require("../utils");
1
+ const fs = require('fs');
2
+ const { merge } = require('lodash');
3
+ const path = require('path');
4
+ const { getRootDir, logFileCrudMessage, logErrorMessage } = require('../utils');
5
5
 
6
6
  module.exports = class ConfigService {
7
7
  _config = { components: {} };
8
8
 
9
- _path = path.join(getRootDir(), "config.json");
9
+ constructor(configFile = 'config.json') {
10
+ this._path = path.join(getRootDir(), configFile);
10
11
 
11
- constructor() {
12
12
  let config;
13
13
 
14
14
  if (!fs.existsSync(this._path)) {
15
- logErrorMessage(`No config file.`);
15
+ logErrorMessage('No config file.');
16
16
  process.exit();
17
17
  }
18
18
 
19
19
  try {
20
20
  config = require(this._path);
21
21
  } catch {
22
- logErrorMessage(`Couldn't parse config file.`);
22
+ logErrorMessage("Couldn't parse config file.");
23
23
  process.exit();
24
24
  }
25
25
 
@@ -35,7 +35,7 @@ module.exports = class ConfigService {
35
35
  }
36
36
 
37
37
  getNewComponentRoot() {
38
- return this._config.newComponentRoot || "components";
38
+ return this._config.newComponentRoot || 'components';
39
39
  }
40
40
 
41
41
  getPrefix() {
@@ -43,15 +43,15 @@ module.exports = class ConfigService {
43
43
  }
44
44
 
45
45
  getApiApplication() {
46
- return this._config.apiApplication || "LtDdZKEPa5lK";
46
+ return this._config.apiApplication || 'LtDdZKEPa5lK';
47
47
  }
48
48
 
49
49
  getApiBaseUrl() {
50
- return this._config.apiBaseUrl || "https://api.ayayot.com";
50
+ return this._config.apiBaseUrl || 'https://api.ayayot.com';
51
51
  }
52
52
 
53
53
  getApiVersion() {
54
- return this._config.apiVersion || "2";
54
+ return this._config.apiVersion || '2';
55
55
  }
56
56
 
57
57
  addComponent(name, config) {
@@ -60,19 +60,15 @@ module.exports = class ConfigService {
60
60
  }
61
61
 
62
62
  extendComponent(name, config) {
63
- this._config.components[name] = merge(
64
- {},
65
- this._config.components[name],
66
- config
67
- );
63
+ this._config.components[name] = merge({}, this._config.components[name], config);
68
64
  this._sync();
69
65
  }
70
66
 
71
67
  _sync() {
72
- fs.writeFileSync(this._path, JSON.stringify(this._config, null, 2) + "\n", {
73
- encoding: "utf8",
74
- flag: "w",
68
+ fs.writeFileSync(this._path, `${JSON.stringify(this._config, null, 2)}\n`, {
69
+ encoding: 'utf8',
70
+ flag: 'w',
75
71
  });
76
- logFileCrudMessage("UPDATE", "config.json");
72
+ logFileCrudMessage('UPDATE', 'config.json');
77
73
  }
78
74
  };
@@ -0,0 +1,28 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const axios = require('axios');
4
+ const { getRootDir, logErrorMessage } = require('./utils');
5
+
6
+ module.exports = function request(options) {
7
+ const customPath = path.join(getRootDir(), 'http-request.js');
8
+
9
+ if (fs.existsSync(customPath)) {
10
+ const customRequest = require(customPath);
11
+
12
+ if (typeof customRequest !== 'function') {
13
+ logErrorMessage('The custom "http-request.js" file must export a function.');
14
+ process.exit();
15
+ }
16
+
17
+ const promise = customRequest(options);
18
+
19
+ if (typeof promise !== 'object' || typeof promise.then !== 'function') {
20
+ logErrorMessage('The exported function in the custom "http-request.js" file must return a Promise.');
21
+ process.exit();
22
+ }
23
+
24
+ return promise;
25
+ }
26
+
27
+ return axios.request(options);
28
+ };
package/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  module.exports = {
2
- AuthService: require("./api/auth.service"),
3
- DeployService: require("./api/deploy.service"),
4
- ConfigService: require("./config/config.service"),
5
- Server: require("./server"),
6
- TemplateService: require("./template/template.service"),
7
- ...require("./meta-files"),
8
- ...require("./prompts"),
9
- ...require("./utils"),
2
+ AuthService: require('./api/auth.service'),
3
+ DeployService: require('./api/deploy.service'),
4
+ ConfigService: require('./config/config.service'),
5
+ Server: require('./server'),
6
+ TemplateService: require('./template/template.service'),
7
+ ...require('./meta-files'),
8
+ ...require('./prompts'),
9
+ ...require('./utils'),
10
10
  };
package/meta-files.js CHANGED
@@ -1,10 +1,14 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const { debounce, uniq } = require("lodash");
4
- const rimraf = require("rimraf");
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { debounce, uniq } = require('lodash');
4
+ const rimraf = require('rimraf');
5
5
 
6
- const ICON_FILE_NAME = "icon.svg";
7
- const MANIFEST_FILE_NAME = "manifest.json";
6
+ const ICON_FILE_NAME = 'icon.svg';
7
+ const MANIFEST_FILE_NAME = 'manifest.json';
8
+ const WATCH_OPTIONS = {
9
+ cwd: require('./utils').getRootDir(),
10
+ usePolling: process.platform !== 'darwin',
11
+ };
8
12
 
9
13
  module.exports = {
10
14
  cleanDir(dir) {
@@ -14,11 +18,11 @@ module.exports = {
14
18
  fs.mkdirSync(dir, { recursive: true });
15
19
  },
16
20
  copyAssets(assets, inputDir, outputDir) {
17
- const paths = uniq([MANIFEST_FILE_NAME, ICON_FILE_NAME, ...assets]).map(
18
- (asset) => path.join(inputDir, asset)
21
+ const paths = uniq([MANIFEST_FILE_NAME, ICON_FILE_NAME, ...assets]).map((asset) =>
22
+ path.join(inputDir, asset),
19
23
  );
20
24
  paths.forEach((_path) => {
21
- require("glob")
25
+ require('glob')
22
26
  .sync(_path)
23
27
  .forEach((file) => {
24
28
  const fileName = file.slice(inputDir.length + 1);
@@ -27,34 +31,36 @@ module.exports = {
27
31
  });
28
32
  },
29
33
  watchAssets(assets, inputDir, outputDir) {
30
- const root = require("./utils").getRootDir();
31
- return require("chokidar")
34
+ const root = require('./utils').getRootDir();
35
+ return require('chokidar')
32
36
  .watch(
33
37
  uniq([MANIFEST_FILE_NAME, ICON_FILE_NAME, ...assets]).map((asset) =>
34
- path.join(inputDir, asset)
38
+ path.join(inputDir, asset),
35
39
  ),
36
- { cwd: require("./utils").getRootDir(), ignoreInitial: true }
40
+ WATCH_OPTIONS,
37
41
  )
38
- .on("all", (type, file) => {
42
+ .on('all', (type, file) => {
39
43
  const fileName = path.join(root, file).slice(inputDir.length + 1);
40
44
  switch (type) {
41
- case "add":
42
- case "change":
45
+ case 'add':
46
+ case 'change':
43
47
  _copyFileNameFromToSync(fileName, inputDir, outputDir);
44
48
  break;
45
- case "unlink":
49
+ case 'unlink':
46
50
  if (fs.existsSync(path.join(outputDir, fileName))) {
47
51
  rimraf.sync(path.join(outputDir, fileName));
48
52
  }
49
53
  break;
54
+ default:
55
+ break;
50
56
  }
51
57
  });
52
58
  },
53
- watchInputDir: function (dir, watchCallback) {
59
+ watchInputDir(dir, watchCallback) {
54
60
  const _debouncedCallback = debounce(() => watchCallback(), 300);
55
- require("chokidar")
56
- .watch([`${dir}/**`], { ignoreInitial: true })
57
- .on("all", (_, _path) => {
61
+ require('chokidar')
62
+ .watch([`${dir}/**`], WATCH_OPTIONS)
63
+ .on('all', (_, _path) => {
58
64
  if (
59
65
  _path.endsWith(path.join(dir, MANIFEST_FILE_NAME)) ||
60
66
  _path.endsWith(path.join(dir, ICON_FILE_NAME))
@@ -67,11 +73,11 @@ module.exports = {
67
73
  },
68
74
  writeDemoFile: function writeDemoFile(tag, outputDir, outputFile) {
69
75
  const demoFileContent = `<meta charset="utf-8">\n<title>${tag} demo</title>\n<script src="./${path.basename(
70
- outputFile
76
+ outputFile,
71
77
  )}"></script>\n\n\n<${tag}></${tag}>\n\n`;
72
78
  fs.writeFileSync(`${outputDir}/demo.html`, demoFileContent, {
73
- encoding: "utf8",
74
- flag: "w",
79
+ encoding: 'utf8',
80
+ flag: 'w',
75
81
  });
76
82
  },
77
83
  };
package/package.json CHANGED
@@ -1,25 +1,24 @@
1
1
  {
2
2
  "name": "@ixon-cdk/core",
3
- "version": "1.0.0-alpha.9",
3
+ "version": "1.1.0-next.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "author": "",
7
7
  "license": "ISC",
8
8
  "dependencies": {
9
9
  "app-root-dir": "^1.0.2",
10
- "archiver": "^5.3.0",
11
- "axios": "^0.21.4",
10
+ "archiver": "^5.3.1",
11
+ "axios": "^0.26.1",
12
12
  "chalk": "^4.1.2",
13
- "chokidar": "^3.5.2",
13
+ "chokidar": "^3.5.3",
14
14
  "crypto-js": "^4.1.1",
15
- "debounce": "^1.2.1",
16
- "express": "^4.17.1",
17
- "glob": "^7.2.0",
15
+ "express": "^4.17.3",
16
+ "glob": "^8.0.1",
18
17
  "livereload": "^0.9.3",
19
18
  "lodash": "^4.17.21",
20
19
  "opener": "^1.5.2",
21
- "prompts": "^2.4.1",
20
+ "prompts": "^2.4.2",
22
21
  "rimraf": "^3.0.2",
23
- "yargs": "^17.1.1"
22
+ "yargs": "^17.4.1"
24
23
  }
25
24
  }
package/prompts.js CHANGED
@@ -1,31 +1,31 @@
1
1
  module.exports = {
2
- promptCompanyId: function(name) {
2
+ promptCompanyId(name) {
3
3
  return {
4
- type: "text",
4
+ type: 'text',
5
5
  name,
6
- message: "What is the ID of your Company?",
6
+ message: 'What is the ID of your Company?',
7
7
  validate: (value) => {
8
8
  if (!value) {
9
- return "Company ID is required.";
9
+ return 'Company ID is required.';
10
10
  }
11
11
  if (!/^[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}$/.test(value)) {
12
- return "Invalid company ID.";
12
+ return 'Invalid company ID.';
13
13
  }
14
14
  return true;
15
15
  },
16
16
  };
17
17
  },
18
- promptPageComponentTemplateId: function(name) {
18
+ promptPageComponentTemplateId(name) {
19
19
  return {
20
- type: "text",
20
+ type: 'text',
21
21
  name,
22
- message: "What is the ID of the PageComponentTemplate?",
22
+ message: 'What is the ID of the PageComponentTemplate?',
23
23
  validate: (value) => {
24
24
  if (!value) {
25
- return "Template ID is required.";
25
+ return 'Template ID is required.';
26
26
  }
27
27
  if (!/^[a-zA-Z0-9]{12}$/.test(value)) {
28
- return "Invalid template ID.";
28
+ return 'Invalid template ID.';
29
29
  }
30
30
  return true;
31
31
  },
package/server/index.js CHANGED
@@ -1,32 +1,34 @@
1
- const path = require("path");
2
- const express = require("express");
3
- const ConfigService = require("../config/config.service");
1
+ const path = require('path');
2
+ const express = require('express');
3
+ const liveReload = require('livereload');
4
+ const ConfigService = require('../config/config.service');
5
+
6
+ const LR_PORT_DEFAULT = 35729;
4
7
 
5
8
  module.exports = class Server {
6
9
  constructor(opts) {
7
10
  this._configSrv = new ConfigService();
8
- this._opts = Object.assign(
9
- { port: 8000, componentBasePath: "components" },
10
- opts
11
- );
11
+ this._rootDir = require('../utils').getRootDir();
12
+ this._opts = {
13
+ port: 8000,
14
+ componentBasePath: 'components',
15
+ ...opts,
16
+ };
12
17
  }
13
18
 
14
19
  serve(names) {
15
20
  const app = express();
16
21
 
17
22
  // Cache
18
- app.use(function (_, res, next) {
19
- res.header("Cache-Control", "no-store");
23
+ app.use((_, res, next) => {
24
+ res.header('Cache-Control', 'no-store');
20
25
  next();
21
26
  });
22
27
 
23
28
  // CORS
24
- app.use(function (_, res, next) {
25
- res.header("Access-Control-Allow-Origin", "*");
26
- res.header(
27
- "Access-Control-Allow-Headers",
28
- "Origin, X-Requested-With, Content-Type, Accept"
29
- );
29
+ app.use((_, res, next) => {
30
+ res.header('Access-Control-Allow-Origin', '*');
31
+ res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
30
32
  next();
31
33
  });
32
34
 
@@ -35,35 +37,33 @@ module.exports = class Server {
35
37
  names.forEach((name) => {
36
38
  const outputDir = this._getOutputDir(name);
37
39
  if (outputDir) {
38
- const dir = path.resolve(require("../utils").getRootDir(), outputDir);
39
- app.use(
40
- `/${this._opts.componentBasePath}/${name}`,
41
- express.static(dir)
42
- );
40
+ const dir = path.resolve(this._rootDir, outputDir);
41
+ app.use(`/${this._opts.componentBasePath}/${name}`, express.static(dir));
43
42
  }
44
43
  });
45
44
  }
46
45
 
47
46
  // Adds live reload watchers
48
- const lrServer = require("livereload").createServer();
49
- const outputs = names.flatMap((name) => [
50
- this._getOutput(name),
51
- `dist/${name}/manifest.json`,
52
- ]);
53
-
54
- const watcher = require("chokidar").watch(outputs, { ignoreInitial: true, usePolling: true });
55
- watcher.on("change", require("debounce")(() => lrServer,refresh("/"), 500));
56
- process.on("exit", () => {
57
- lrServer.close();
58
- watcher.close();
47
+ const lrServer = liveReload.createServer({
48
+ port: this._opts.liveReloadPort,
59
49
  });
50
+ const _refresh = () => lrServer.refresh('/');
51
+ const _debouncedRefresh = require('lodash/debounce')(_refresh, 250);
52
+ names
53
+ .flatMap((name) => [
54
+ [path.join(this._rootDir, this._getOutput(name)), 100],
55
+ [path.join(this._rootDir, `dist/${name}/manifest.json`), 500],
56
+ ])
57
+ .forEach(([filename, interval]) =>
58
+ require('fs').watchFile(filename, { interval }, _debouncedRefresh),
59
+ );
60
60
 
61
61
  // Simulator app
62
- const appDir = path.dirname(require.resolve("@ixon-cdk/simulator"));
63
- app.get(`/${this._opts.componentBasePath}/*`, function (req, res) {
62
+ const appDir = path.dirname(require.resolve('@ixon-cdk/simulator'));
63
+ app.get(`/${this._opts.componentBasePath}/*`, (req, res) => {
64
64
  res.sendStatus(404);
65
65
  });
66
- app.get(`/config.json`, (req, res) => {
66
+ app.get('/config.json', (req, res) => {
67
67
  res.send({
68
68
  api: {
69
69
  appId: this._configSrv.getApiApplication(),
@@ -72,22 +72,32 @@ module.exports = class Server {
72
72
  },
73
73
  });
74
74
  });
75
- app.get("*.*", express.static(appDir));
76
- app.all("*", function (req, res) {
77
- res.status(200).sendFile(`/index.html`, { root: appDir });
75
+ app.get('*.*', express.static(appDir));
76
+ app.all('*', (req, res) => {
77
+ res.status(200).sendFile('/index.html', { root: appDir });
78
78
  });
79
79
 
80
- app.listen(this._opts.port);
80
+ this.server = app.listen(this._opts.port);
81
81
  }
82
82
 
83
83
  openSimulator(name = null) {
84
84
  const url = this._getComponentUrl(name);
85
- const queryString = url ? `?pct-url=${encodeURIComponent(url)}` : "";
86
- require("opener")(`${this._getBaseUrl()}/${queryString}`);
85
+ const lrPort = this._opts.liveReloadPort;
86
+ const params = new URLSearchParams();
87
+ if (url) {
88
+ params.set('pct-url', url);
89
+ }
90
+ if (lrPort !== LR_PORT_DEFAULT) {
91
+ params.set('lr-port', lrPort);
92
+ }
93
+ const queryString = params.toString();
94
+ const openUrl = `${this._getBaseUrl()}/?${queryString}`;
95
+ require('opener')(openUrl);
87
96
  }
88
97
 
89
98
  _getBaseUrl() {
90
- return `http://localhost:${this._opts.port}`;
99
+ const address = this.server.address();
100
+ return `http://localhost:${address.port}`;
91
101
  }
92
102
 
93
103
  _getComponentUrl(name) {
@@ -1,31 +1,152 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const prompts = require("prompts");
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const prompts = require('prompts');
4
4
 
5
5
  const {
6
+ getFiles,
6
7
  getRootDir,
7
8
  ensureModule,
9
+ logErrorMessage,
8
10
  logFileCrudMessage,
9
11
  pascalCase,
10
- } = require("../utils");
11
- const ConfigService = require("../config/config.service");
12
+ } = require('../utils');
13
+ const ConfigService = require('../config/config.service');
12
14
 
13
15
  module.exports = class TemplateService {
14
16
  _configSrv = new ConfigService();
15
17
 
18
+ _rootDir = getRootDir();
19
+
16
20
  _schemas = {};
17
21
 
18
22
  constructor() {
19
23
  this._discover();
20
24
  }
21
25
 
26
+ async addFromExample(componentName, componentPrefix) {
27
+ const examplesRoot = path.join(this._rootDir, 'node_modules/component-examples');
28
+
29
+ if (!fs.existsSync(examplesRoot)) {
30
+ logErrorMessage("Package 'ixoncloud/component-examples' is not installed.");
31
+ process.exit();
32
+ }
33
+
34
+ const examplesConfigSrv = new ConfigService('node_modules/component-examples/config.json');
35
+ const { components } = examplesConfigSrv._config;
36
+
37
+ if (!Object.keys(components).length) {
38
+ logErrorMessage('No examples found.');
39
+ process.exit();
40
+ }
41
+
42
+ const exampleComponentRoot = examplesConfigSrv.getNewComponentRoot();
43
+ const examplePrefix = examplesConfigSrv.getPrefix();
44
+
45
+ const result = await prompts({
46
+ type: 'select',
47
+ name: 'name',
48
+ message: 'Pick a template',
49
+ choices: Object.keys(components).map((name) => ({ title: name, value: name })),
50
+ initial: 0,
51
+ });
52
+ const exampleName = result.name;
53
+
54
+ if (!exampleName) {
55
+ process.exit();
56
+ } else if (!components[exampleName]) {
57
+ logErrorMessage(`Example '${exampleName}' is not configured.`);
58
+ process.exit();
59
+ }
60
+
61
+ const { build } = components[exampleName].runner;
62
+ const { builder } = build;
63
+ const isVue = builder.startsWith('@ixon-cdk/vue-builder');
64
+ const isStatic = builder.startsWith('@ixon-cdk/static-builder');
65
+ const isSvelte = builder.startsWith('@ixon-cdk/svelte-builder');
66
+
67
+ // find source files, read and replace.
68
+ const exampleDir = path.join(examplesRoot, exampleComponentRoot, exampleName);
69
+ const inputFilePath = path.join(exampleDir, build.input);
70
+ let inputFileContents = fs.readFileSync(inputFilePath, { encoding: 'utf-8' });
71
+
72
+ if (isVue) {
73
+ inputFileContents = this._findAndReplaceVueInputFile(inputFileContents, componentName);
74
+ } else if (isStatic) {
75
+ inputFileContents = this._findAndReplaceStaticInputFile(
76
+ inputFileContents,
77
+ examplePrefix,
78
+ exampleName,
79
+ componentPrefix,
80
+ componentName,
81
+ );
82
+ }
83
+
84
+ // find manifest file, read and replace.
85
+ const manifestFilePath = path.join(path.dirname(inputFilePath), 'manifest.json');
86
+ const manifestFileContents = JSON.parse(fs.readFileSync(manifestFilePath));
87
+ manifestFileContents.main = `${this._getTag(componentPrefix, componentName)}.min.js`;
88
+ manifestFileContents.version = '1';
89
+
90
+ let input;
91
+ if (isVue) {
92
+ input = `${componentName}.vue`;
93
+ } else if (isStatic) {
94
+ input = `${componentName}.js`;
95
+ } else if (isSvelte) {
96
+ input = `${componentName}.svelte`;
97
+ }
98
+
99
+ const files = await getFiles(exampleDir).catch(logErrorMessage);
100
+
101
+ // find and install any missing dependencies.
102
+ files.forEach((file) => {
103
+ if (/\.(jsx?|tsx?|svelte|vue)$/i.test(file)) {
104
+ const fileContents = fs.readFileSync(file);
105
+ const { dependencies } = require(path.join(examplesRoot, 'package.json'));
106
+ this._checkDependencies(fileContents, dependencies || {});
107
+ }
108
+ });
109
+
110
+ // else start copying input, and menifest files
111
+ const componentRoot = this._configSrv.getNewComponentRoot();
112
+ const dir = path.join(this._rootDir, componentRoot, componentName);
113
+ const _logCreatedInDir = (fileName) => {
114
+ logFileCrudMessage('CREATE', path.join(componentRoot, componentName, fileName));
115
+ };
116
+ fs.mkdirSync(dir, { recursive: true });
117
+
118
+ files
119
+ .map((file) => file.slice(exampleDir.length + 1))
120
+ .forEach((file) => {
121
+ // create directory if it doesn't exist.
122
+ const _dir = path.dirname(path.join(dir, file));
123
+ if (!fs.existsSync(_dir)) {
124
+ fs.mkdirSync(_dir, { recursive: true });
125
+ }
126
+ // for the input-file and manifest use the replaced file contents.
127
+ if (file === build.input) {
128
+ fs.writeFileSync(path.join(dir, input), inputFileContents, { encoding: 'utf-8' });
129
+ _logCreatedInDir(input);
130
+ } else if (file === 'manifest.json') {
131
+ const json = `${JSON.stringify(manifestFileContents, null, 2)}\n`;
132
+ fs.writeFileSync(path.join(dir, file), json, { encoding: 'utf-8' });
133
+ _logCreatedInDir(file);
134
+ } else {
135
+ fs.copyFileSync(path.join(exampleDir, file), path.join(dir, file));
136
+ _logCreatedInDir(file);
137
+ }
138
+ });
139
+
140
+ return Promise.resolve({ config: { runner: { build: { builder, input } } } });
141
+ }
142
+
22
143
  async add(componentName, componentPrefix) {
23
144
  let schema;
24
145
 
25
146
  const result = await prompts({
26
- type: "select",
27
- name: "templateName",
28
- message: "Pick a template",
147
+ type: 'select',
148
+ name: 'templateName',
149
+ message: 'Pick a template',
29
150
  choices: Object.keys(this._schemas).map((key) => ({
30
151
  title: this._schemas[key].name,
31
152
  value: key,
@@ -33,23 +154,19 @@ module.exports = class TemplateService {
33
154
  initial: 0,
34
155
  });
35
156
 
36
- const tag = componentPrefix
37
- ? `${componentPrefix}-${componentName}`
38
- : componentName;
39
-
40
157
  if (result) {
41
158
  schema = this._schemas[result.templateName];
42
159
  }
43
160
 
44
161
  if (schema) {
45
- const context = { name: componentName, tag };
162
+ const context = { name: componentName, tag: this._getTag(componentPrefix, componentName) };
46
163
  let variantIdx = null;
47
164
 
48
165
  if (schema.variants) {
49
166
  const _result = await prompts({
50
- type: "select",
51
- name: "variantIdx",
52
- message: "Pick a variant",
167
+ type: 'select',
168
+ name: 'variantIdx',
169
+ message: 'Pick a variant',
53
170
  choices: schema.variants.map((variant, index) => ({
54
171
  title: variant.name,
55
172
  value: index,
@@ -65,31 +182,28 @@ module.exports = class TemplateService {
65
182
  schema = this._interpolateSchema(schema, context);
66
183
 
67
184
  if (schema.config.runner) {
68
- const modulesNames = Object.keys(schema.config.runner).reduce(
69
- (names, cmd) => {
70
- if (schema.config.runner[cmd].builder) {
71
- const name = schema.config.runner[cmd].builder.split(":")[0];
72
- if (!names.includes(name)) {
73
- return [...names, name];
74
- }
185
+ const modulesNames = Object.keys(schema.config.runner).reduce((names, cmd) => {
186
+ if (schema.config.runner[cmd].builder) {
187
+ const name = schema.config.runner[cmd].builder.split(':')[0];
188
+ if (!names.includes(name)) {
189
+ return [...names, name];
75
190
  }
76
- return names;
77
- },
78
- []
79
- );
191
+ }
192
+ return names;
193
+ }, []);
80
194
  modulesNames.forEach((name) => ensureModule(name));
81
195
  }
82
196
 
83
- const root = this._configSrv.getNewComponentRoot();
197
+ const componentRoot = this._configSrv.getNewComponentRoot();
84
198
 
85
199
  schema.files.forEach((file) => {
86
- file.dest = `${root}/${componentName}/${file.dest}`;
200
+ file.dest = `${componentRoot}/${componentName}/${file.dest}`;
87
201
  this.createFile(file, context);
88
202
  });
89
203
 
90
204
  if (variantIdx !== null) {
91
205
  schema.variants[variantIdx].files.forEach((file) => {
92
- file.dest = `${root}/${componentName}/${file.dest}`;
206
+ file.dest = `${componentRoot}/${componentName}/${file.dest}`;
93
207
  this.createFile(file, context);
94
208
  });
95
209
  }
@@ -99,56 +213,126 @@ module.exports = class TemplateService {
99
213
  }
100
214
 
101
215
  createFile(file, ctx) {
102
- const rootDir = getRootDir();
103
- fs.mkdirSync(path.dirname(path.join(rootDir, file.dest)), {
216
+ fs.mkdirSync(path.dirname(path.join(this._rootDir, file.dest)), {
104
217
  recursive: true,
105
218
  });
106
219
  if (file.interpolateContent) {
107
220
  const text = fs.readFileSync(
108
- path.join(
109
- path.dirname(require.resolve("@ixon-cdk/templates")),
110
- file.source
111
- ),
112
- { encoding: "utf-8" }
221
+ path.join(path.dirname(require.resolve('@ixon-cdk/templates')), file.source),
222
+ { encoding: 'utf-8' },
113
223
  );
114
224
  const data = this._interpolateText(text, ctx);
115
- fs.writeFileSync(path.join(rootDir, file.dest), data, {
116
- encoding: "utf-8",
225
+ fs.writeFileSync(path.join(this._rootDir, file.dest), data, {
226
+ encoding: 'utf-8',
117
227
  });
118
228
  } else {
119
229
  fs.copyFileSync(
120
- path.join(
121
- path.dirname(require.resolve("@ixon-cdk/templates")),
122
- file.source
123
- ),
124
- path.join(rootDir, file.dest)
230
+ path.join(path.dirname(require.resolve('@ixon-cdk/templates')), file.source),
231
+ path.join(this._rootDir, file.dest),
125
232
  );
126
233
  }
127
- logFileCrudMessage("CREATE", file.dest);
234
+ logFileCrudMessage('CREATE', file.dest);
235
+ }
236
+
237
+ /**
238
+ * This script loops over a dependencies object and will check if the provided file contents is
239
+ * importing any. When that is the case, it will first check for that package whether it could
240
+ * already be a depencency in your workspace. If not, the package will get installed and saved.
241
+ */
242
+ _checkDependencies(fileContents, dependencies) {
243
+ const rootDeps = require(path.join(this._rootDir, 'package.json')).dependencies || {};
244
+ Object.keys(dependencies).forEach((pkg) => {
245
+ const matcher = new RegExp(`^\\s*import.*\\s['"]${pkg}\\S*['"]`, 'gm');
246
+ if (matcher.test(fileContents) && !(pkg in rootDeps)) {
247
+ const version = dependencies[pkg];
248
+ console.log(`Installing package '${pkg}'...`);
249
+ require('child_process').execSync(`npm i ${pkg}@${version} --save`);
250
+ }
251
+ });
252
+ }
253
+
254
+ /**
255
+ * This script will find and replace the component name definition in a vue (SFC) input file.
256
+ */
257
+ _findAndReplaceVueInputFile(fileContents, componentName) {
258
+ const matcher = /export\s+default\s+{\s+name:\s+['"](\S+)['"]/gm;
259
+ return fileContents.replaceAll(matcher, (match, p1) => match.replace(p1, componentName));
260
+ }
261
+
262
+ /**
263
+ * This script will find and replace the component class definition and the arguments for the
264
+ * custom element define method in a static input file.
265
+ *
266
+ * Given the example input file has the follwing contents:
267
+ *
268
+ * ```js
269
+ * class PctExample extends HTMLElement {
270
+ * ...
271
+ * }
272
+ *
273
+ * customElements.define('pct-example', PctExample);
274
+ * ```
275
+ *
276
+ * ...if your workspace prefix is "abc" and your component name is "from-example", the content
277
+ * will be transformed into:
278
+ *
279
+ * ```js
280
+ * class AbcFromExample extends HTMLElement {
281
+ * ...
282
+ * }
283
+ *
284
+ * customElements.define('abc-from-example', AbcFromExample);
285
+ * ```
286
+ */
287
+ _findAndReplaceStaticInputFile(
288
+ fileContents,
289
+ searchPrefix,
290
+ searchName,
291
+ replacementPrefix,
292
+ replacementName,
293
+ ) {
294
+ const searchTag = this._getTag(searchPrefix, searchName);
295
+ const replacementTag = this._getTag(replacementPrefix, replacementName);
296
+ return fileContents
297
+ .replaceAll(new RegExp(`class\\s+(${pascalCase(searchTag)})`, 'gm'), (match, p1) =>
298
+ match.replace(p1, pascalCase(replacementTag)),
299
+ )
300
+ .replaceAll(
301
+ new RegExp(
302
+ `define\\s*\\(\\s*['"](${searchTag})['"]\\s*,\\s+(${pascalCase(searchTag)})\\)`,
303
+ 'gm',
304
+ ),
305
+ (match, p1, p2) =>
306
+ match.replace(p1, replacementTag).replace(p2, pascalCase(replacementTag)),
307
+ );
128
308
  }
129
309
 
130
310
  _discover() {
131
- const dir = path.dirname(require.resolve("@ixon-cdk/templates"));
311
+ const dir = path.dirname(require.resolve('@ixon-cdk/templates'));
132
312
  const files = fs.readdirSync(dir);
133
313
  files.forEach((file) => {
134
314
  if (fs.lstatSync(path.join(dir, file)).isDirectory()) {
135
- const schemaFile = path.join(dir, file, "schema.json");
315
+ const schemaFile = path.join(dir, file, 'schema.json');
136
316
  if (fs.existsSync(schemaFile)) {
137
317
  const schema = JSON.parse(fs.readFileSync(schemaFile));
138
- this._schemas = Object.assign({}, this._schemas, { [file]: schema });
318
+ this._schemas = { ...this._schemas, [file]: schema };
139
319
  }
140
320
  }
141
321
  });
142
322
  }
143
323
 
324
+ _getTag(prefix, name) {
325
+ return prefix ? `${prefix}-${name}` : name;
326
+ }
327
+
144
328
  _interpolateSchema(schema, params) {
145
329
  return JSON.parse(this._interpolateText(JSON.stringify(schema), params));
146
330
  }
147
331
 
148
332
  _interpolateText(text, params) {
149
333
  return text
150
- .replace(/\{name\}/g, params.name)
151
- .replace(/\{tag\}/g, params.tag)
152
- .replace(/\{pascalCase\(tag\)\}/g, pascalCase(params.tag));
334
+ .replace(/<%=\s*name\s*%>/g, params.name)
335
+ .replace(/<%=\s*tag\s*%>/g, params.tag)
336
+ .replace(/<%=\s*classify\(\s*tag\s*\)\s*%>/g, pascalCase(params.tag));
153
337
  }
154
338
  };
package/utils.js CHANGED
@@ -1,20 +1,18 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const chalk = require("chalk");
4
- const flow = require("lodash/flow");
5
- const camelCase = require("lodash/camelCase");
6
- const upperFirst = require("lodash/upperFirst");
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const flow = require('lodash/flow');
5
+ const camelCase = require('lodash/camelCase');
6
+ const upperFirst = require('lodash/upperFirst');
7
7
 
8
8
  function getArgv() {
9
9
  const remain = process.argv.slice(2);
10
- const argv = require("yargs/yargs")(remain).argv;
10
+ const { argv } = require('yargs/yargs')(remain);
11
11
 
12
12
  // version argument fix
13
- if ("version" in argv) {
14
- const versionArg = remain.find((arg) =>
15
- /^--version=("[0-9]+"|'[0-9]+'|[0-9]+)$/.test(arg)
16
- );
17
- if (!!versionArg) {
13
+ if ('version' in argv) {
14
+ const versionArg = remain.find((arg) => /^--version=("[0-9]+"|'[0-9]+'|[0-9]+)$/.test(arg));
15
+ if (versionArg) {
18
16
  const matches = versionArg.match(/^--version=['"]?([0-9]+)['"]?$/);
19
17
  argv.version = Number(matches[1]);
20
18
  }
@@ -24,7 +22,7 @@ function getArgv() {
24
22
  }
25
23
 
26
24
  function getRootDir() {
27
- return require("app-root-dir").get();
25
+ return require('app-root-dir').get();
28
26
  }
29
27
 
30
28
  function logErrorMessage(message) {
@@ -33,10 +31,12 @@ function logErrorMessage(message) {
33
31
 
34
32
  function logFileCrudMessage(crud, file) {
35
33
  switch (crud) {
36
- case "CREATE":
37
- case "UPDATE":
34
+ case 'CREATE':
35
+ case 'UPDATE':
38
36
  console.log(`${chalk.green(crud)} ${file}`);
39
37
  break;
38
+ default:
39
+ break;
40
40
  }
41
41
  }
42
42
 
@@ -55,24 +55,30 @@ function moduleExists(moduleName) {
55
55
  }
56
56
 
57
57
  function ensureModule(moduleName) {
58
- if (!moduleName.startsWith("@ixon-cdk/")) {
59
- return logErrorMessage("Cannot install this module.");
58
+ if (!moduleName.startsWith('@ixon-cdk/')) {
59
+ logErrorMessage('Cannot install this module.');
60
+ return;
60
61
  }
61
62
  if (!moduleExists(moduleName)) {
62
63
  console.log(`Installing package '${moduleName}'...`);
63
- require("child_process").execSync(`npm install --save-dev ${moduleName}`);
64
+ if (moduleName.startsWith('@ixon-cdk/')) {
65
+ const cdkVersion = require('./package.json').version;
66
+ require('child_process').execSync(`npm install --save-dev ${moduleName}@${cdkVersion}`);
67
+ } else {
68
+ require('child_process').execSync(`npm install --save-dev ${moduleName}`);
69
+ }
64
70
  }
65
71
  }
66
72
 
67
73
  function zip(output, callback) {
68
74
  const outputDir = path.dirname(output);
69
- const zipFile = path.join(outputDir + ".zip");
75
+ const zipFile = path.join(`${outputDir}.zip`);
70
76
  const stream = fs.createWriteStream(zipFile);
71
- stream.on("close", () => {
72
- require("rimraf").sync(outputDir);
77
+ stream.on('close', () => {
78
+ require('rimraf').sync(outputDir);
73
79
  callback(zipFile);
74
80
  });
75
- const archive = require("archiver")("zip", { zlib: { level: 9 } });
81
+ const archive = require('archiver')('zip', { zlib: { level: 9 } });
76
82
  archive.pipe(stream);
77
83
  archive.directory(outputDir, path.basename(outputDir));
78
84
  archive.finalize();
@@ -81,7 +87,7 @@ function zip(output, callback) {
81
87
  const pascalCase = flow(camelCase, upperFirst);
82
88
 
83
89
  function apiHttpErrorToMessage(error) {
84
- if (typeof error === "string") {
90
+ if (typeof error === 'string') {
85
91
  logErrorMessage(error);
86
92
  process.exit();
87
93
  }
@@ -90,28 +96,18 @@ function apiHttpErrorToMessage(error) {
90
96
  if (errors) {
91
97
  logErrorMessage(
92
98
  errors
93
- .map((err) =>
94
- !!err.propertyName
95
- ? `${err.propertyName}: ${err.message}`
96
- : err.message
97
- )
98
- .join("\n")
99
+ .map((err) => (err.propertyName ? `${err.propertyName}: ${err.message}` : err.message))
100
+ .join('\n'),
99
101
  );
100
102
  process.exit();
101
103
  }
102
104
  }
103
- logErrorMessage("Unexpected error.");
105
+ logErrorMessage('Unexpected error.');
104
106
  console.log(error);
105
107
  process.exit();
106
108
  }
107
109
 
108
- function generatePreviewHash(
109
- templateId,
110
- templateName,
111
- versionId,
112
- versionNumber,
113
- versionMainPath
114
- ) {
110
+ function generatePreviewHash(templateId, templateName, versionId, versionNumber, versionMainPath) {
115
111
  const ref = {
116
112
  tid: templateId,
117
113
  tnm: templateName,
@@ -119,14 +115,26 @@ function generatePreviewHash(
119
115
  vnr: versionNumber,
120
116
  vmp: versionMainPath,
121
117
  };
122
- const salt = "A9qJ03jh";
123
- return require("crypto-js").AES.encrypt(JSON.stringify(ref), salt).toString();
118
+ const salt = 'A9qJ03jh';
119
+ return require('crypto-js').AES.encrypt(JSON.stringify(ref), salt).toString();
120
+ }
121
+
122
+ async function getFiles(dir) {
123
+ const dirents = await fs.promises.readdir(dir, { withFileTypes: true });
124
+ const files = await Promise.all(
125
+ dirents.map((dirent) => {
126
+ const res = path.resolve(dir, dirent.name);
127
+ return dirent.isDirectory() ? getFiles(res) : res;
128
+ }),
129
+ );
130
+ return Array.prototype.concat(...files);
124
131
  }
125
132
 
126
133
  module.exports = {
127
134
  getArgv,
128
135
  getRootDir,
129
136
  logErrorMessage,
137
+ getFiles,
130
138
  logFileCrudMessage,
131
139
  logSuccessMessage,
132
140
  moduleExists,