@hubspot/local-dev-lib 0.0.1-experimental.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/LICENSE +12 -0
  2. package/README.md +17 -0
  3. package/api/appsDev.d.ts +6 -0
  4. package/api/appsDev.js +29 -0
  5. package/api/customObjects.d.ts +8 -0
  6. package/api/customObjects.js +45 -0
  7. package/api/designManager.d.ts +5 -0
  8. package/api/designManager.js +18 -0
  9. package/api/developerTestAccounts.d.ts +7 -0
  10. package/api/developerTestAccounts.js +48 -0
  11. package/api/fileManager.d.ts +6 -0
  12. package/api/fileManager.js +63 -0
  13. package/api/fileMapper.d.ts +12 -0
  14. package/api/fileMapper.js +106 -0
  15. package/api/fileTransport.d.ts +4 -0
  16. package/api/fileTransport.js +39 -0
  17. package/api/functions.d.ts +8 -0
  18. package/api/functions.js +43 -0
  19. package/api/github.d.ts +11 -0
  20. package/api/github.js +71 -0
  21. package/api/hubdb.d.ts +12 -0
  22. package/api/hubdb.js +67 -0
  23. package/api/lighthouseScore.d.ts +6 -0
  24. package/api/lighthouseScore.js +26 -0
  25. package/api/localDevAuth.d.ts +8 -0
  26. package/api/localDevAuth.js +53 -0
  27. package/api/marketplaceValidation.d.ts +6 -0
  28. package/api/marketplaceValidation.js +26 -0
  29. package/api/projects.d.ts +40 -0
  30. package/api/projects.js +216 -0
  31. package/api/sandboxHubs.d.ts +7 -0
  32. package/api/sandboxHubs.js +49 -0
  33. package/api/sandboxSync.d.ts +4 -0
  34. package/api/sandboxSync.js +26 -0
  35. package/api/secrets.d.ts +6 -0
  36. package/api/secrets.js +37 -0
  37. package/api/validateHubl.d.ts +3 -0
  38. package/api/validateHubl.js +15 -0
  39. package/config/CLIConfiguration.d.ts +62 -0
  40. package/config/CLIConfiguration.js +467 -0
  41. package/config/configFile.d.ts +21 -0
  42. package/config/configFile.js +102 -0
  43. package/config/configUtils.d.ts +5 -0
  44. package/config/configUtils.js +87 -0
  45. package/config/config_DEPRECATED.d.ts +75 -0
  46. package/config/config_DEPRECATED.js +678 -0
  47. package/config/environment.d.ts +2 -0
  48. package/config/environment.js +60 -0
  49. package/config/getAccountIdentifier.d.ts +2 -0
  50. package/config/getAccountIdentifier.js +15 -0
  51. package/config/index.d.ts +41 -0
  52. package/config/index.js +260 -0
  53. package/constants/api.d.ts +15 -0
  54. package/constants/api.js +18 -0
  55. package/constants/auth.d.ts +37 -0
  56. package/constants/auth.js +38 -0
  57. package/constants/config.d.ts +18 -0
  58. package/constants/config.js +22 -0
  59. package/constants/environments.d.ts +15 -0
  60. package/constants/environments.js +18 -0
  61. package/constants/extensions.d.ts +6 -0
  62. package/constants/extensions.js +28 -0
  63. package/constants/files.d.ts +21 -0
  64. package/constants/files.js +24 -0
  65. package/constants/ports.d.ts +3 -0
  66. package/constants/ports.js +6 -0
  67. package/enums/build.d.ts +36 -0
  68. package/enums/build.js +39 -0
  69. package/enums/deploy.d.ts +11 -0
  70. package/enums/deploy.js +14 -0
  71. package/enums/project.d.ts +6 -0
  72. package/enums/project.js +9 -0
  73. package/errors/errors_DEPRECATED.d.ts +3 -0
  74. package/errors/errors_DEPRECATED.js +60 -0
  75. package/errors/index.d.ts +18 -0
  76. package/errors/index.js +63 -0
  77. package/http/addQueryParams.d.ts +2 -0
  78. package/http/addQueryParams.js +14 -0
  79. package/http/getAxiosConfig.d.ts +9 -0
  80. package/http/getAxiosConfig.js +66 -0
  81. package/http/index.d.ts +17 -0
  82. package/http/index.js +173 -0
  83. package/http/unauthed.d.ts +15 -0
  84. package/http/unauthed.js +38 -0
  85. package/lang/en.json +389 -0
  86. package/lib/archive.d.ts +3 -0
  87. package/lib/archive.js +117 -0
  88. package/lib/cms/functions.d.ts +8 -0
  89. package/lib/cms/functions.js +181 -0
  90. package/lib/cms/handleFieldsJS.d.ts +33 -0
  91. package/lib/cms/handleFieldsJS.js +148 -0
  92. package/lib/cms/modules.d.ts +14 -0
  93. package/lib/cms/modules.js +186 -0
  94. package/lib/cms/processFieldsJs.d.ts +1 -0
  95. package/lib/cms/processFieldsJs.js +97 -0
  96. package/lib/cms/templates.d.ts +65 -0
  97. package/lib/cms/templates.js +107 -0
  98. package/lib/cms/themes.d.ts +2 -0
  99. package/lib/cms/themes.js +34 -0
  100. package/lib/cms/uploadFolder.d.ts +7 -0
  101. package/lib/cms/uploadFolder.js +202 -0
  102. package/lib/cms/validate.d.ts +2 -0
  103. package/lib/cms/validate.js +40 -0
  104. package/lib/cms/watch.d.ts +4 -0
  105. package/lib/cms/watch.js +201 -0
  106. package/lib/customObjects.d.ts +5 -0
  107. package/lib/customObjects.js +42 -0
  108. package/lib/environment.d.ts +2 -0
  109. package/lib/environment.js +16 -0
  110. package/lib/escapeRegExp.d.ts +1 -0
  111. package/lib/escapeRegExp.js +7 -0
  112. package/lib/fileManager.d.ts +2 -0
  113. package/lib/fileManager.js +184 -0
  114. package/lib/fileMapper.d.ts +18 -0
  115. package/lib/fileMapper.js +317 -0
  116. package/lib/fs.d.ts +4 -0
  117. package/lib/fs.js +71 -0
  118. package/lib/github.d.ts +8 -0
  119. package/lib/github.js +167 -0
  120. package/lib/gitignore.d.ts +3 -0
  121. package/lib/gitignore.js +49 -0
  122. package/lib/hubdb.d.ts +17 -0
  123. package/lib/hubdb.js +133 -0
  124. package/lib/ignoreRules.d.ts +3 -0
  125. package/lib/ignoreRules.js +69 -0
  126. package/lib/logger.d.ts +56 -0
  127. package/lib/logger.js +146 -0
  128. package/lib/notify.d.ts +1 -0
  129. package/lib/notify.js +43 -0
  130. package/lib/oauth.d.ts +4 -0
  131. package/lib/oauth.js +34 -0
  132. package/lib/path.d.ts +14 -0
  133. package/lib/path.js +134 -0
  134. package/lib/personalAccessKey.d.ts +10 -0
  135. package/lib/personalAccessKey.js +163 -0
  136. package/lib/portManager.d.ts +10 -0
  137. package/lib/portManager.js +46 -0
  138. package/lib/text.d.ts +2 -0
  139. package/lib/text.js +24 -0
  140. package/lib/trackUsage.d.ts +1 -0
  141. package/lib/trackUsage.js +63 -0
  142. package/lib/urls.d.ts +2 -0
  143. package/lib/urls.js +24 -0
  144. package/models/FileSystemError.d.ts +6 -0
  145. package/models/FileSystemError.js +47 -0
  146. package/models/HubSpotHttpError.d.ts +24 -0
  147. package/models/HubSpotHttpError.js +197 -0
  148. package/models/OAuth2Manager.d.ts +12 -0
  149. package/models/OAuth2Manager.js +105 -0
  150. package/package.json +81 -0
  151. package/types/Accounts.d.ts +178 -0
  152. package/types/Accounts.js +2 -0
  153. package/types/Activity.d.ts +20 -0
  154. package/types/Activity.js +2 -0
  155. package/types/Api.d.ts +2 -0
  156. package/types/Api.js +2 -0
  157. package/types/Apps.d.ts +77 -0
  158. package/types/Apps.js +2 -0
  159. package/types/Archive.d.ts +9 -0
  160. package/types/Archive.js +2 -0
  161. package/types/Build.d.ts +41 -0
  162. package/types/Build.js +2 -0
  163. package/types/CLIOptions.d.ts +8 -0
  164. package/types/CLIOptions.js +2 -0
  165. package/types/ComponentStructure.d.ts +40 -0
  166. package/types/ComponentStructure.js +2 -0
  167. package/types/Config.d.ts +37 -0
  168. package/types/Config.js +2 -0
  169. package/types/Deploy.d.ts +42 -0
  170. package/types/Deploy.js +2 -0
  171. package/types/DesignManager.d.ts +10 -0
  172. package/types/DesignManager.js +2 -0
  173. package/types/Error.d.ts +37 -0
  174. package/types/Error.js +2 -0
  175. package/types/FieldsJS.d.ts +1 -0
  176. package/types/FieldsJS.js +2 -0
  177. package/types/FileManager.d.ts +71 -0
  178. package/types/FileManager.js +2 -0
  179. package/types/Files.d.ts +79 -0
  180. package/types/Files.js +2 -0
  181. package/types/Functions.d.ts +66 -0
  182. package/types/Functions.js +2 -0
  183. package/types/Github.d.ts +76 -0
  184. package/types/Github.js +2 -0
  185. package/types/Http.d.ts +29 -0
  186. package/types/Http.js +2 -0
  187. package/types/Hubdb.d.ts +109 -0
  188. package/types/Hubdb.js +2 -0
  189. package/types/HublValidation.d.ts +59 -0
  190. package/types/HublValidation.js +2 -0
  191. package/types/Lang.d.ts +10 -0
  192. package/types/Lang.js +2 -0
  193. package/types/Lighthouse.d.ts +25 -0
  194. package/types/Lighthouse.js +2 -0
  195. package/types/MarketplaceValidation.d.ts +28 -0
  196. package/types/MarketplaceValidation.js +2 -0
  197. package/types/Migration.d.ts +28 -0
  198. package/types/Migration.js +10 -0
  199. package/types/Modules.d.ts +16 -0
  200. package/types/Modules.js +2 -0
  201. package/types/PortManager.d.ts +11 -0
  202. package/types/PortManager.js +2 -0
  203. package/types/Project.d.ts +42 -0
  204. package/types/Project.js +2 -0
  205. package/types/ProjectLog.d.ts +9 -0
  206. package/types/ProjectLog.js +2 -0
  207. package/types/Sandbox.d.ts +155 -0
  208. package/types/Sandbox.js +2 -0
  209. package/types/Schemas.d.ts +39 -0
  210. package/types/Schemas.js +2 -0
  211. package/types/Secrets.d.ts +3 -0
  212. package/types/Secrets.js +2 -0
  213. package/types/Utils.d.ts +6 -0
  214. package/types/Utils.js +2 -0
  215. package/types/developerTestAccounts.d.ts +12 -0
  216. package/types/developerTestAccounts.js +2 -0
  217. package/utils/PortManagerServer.d.ts +26 -0
  218. package/utils/PortManagerServer.js +158 -0
  219. package/utils/accounts.d.ts +4 -0
  220. package/utils/accounts.js +28 -0
  221. package/utils/cms/fieldsJS.d.ts +2 -0
  222. package/utils/cms/fieldsJS.js +18 -0
  223. package/utils/cms/modules.d.ts +4 -0
  224. package/utils/cms/modules.js +54 -0
  225. package/utils/detectPort.d.ts +1 -0
  226. package/utils/detectPort.js +102 -0
  227. package/utils/git.d.ts +3 -0
  228. package/utils/git.js +71 -0
  229. package/utils/lang.d.ts +6 -0
  230. package/utils/lang.js +88 -0
@@ -0,0 +1,65 @@
1
+ export declare const ANNOTATION_KEYS: {
2
+ isAvailableForNewContent: string;
3
+ templateType: string;
4
+ label: string;
5
+ screenshotPath: string;
6
+ description: string;
7
+ };
8
+ export declare function getAnnotationValue(annotations: string, key: string): string | null;
9
+ export declare function isCodedFile(filePath: string): boolean;
10
+ declare const ASSET_PATHS: {
11
+ readonly 'page-template': "templates/page-template.html";
12
+ readonly partial: "templates/partial.html";
13
+ readonly 'global-partial': "templates/global-partial.html";
14
+ readonly 'email-template': "templates/email-template.html";
15
+ readonly 'blog-listing-template': "templates/blog-listing-template.html";
16
+ readonly 'blog-post-template': "templates/blog-post-template.html";
17
+ readonly 'search-template': "templates/search-template.html";
18
+ readonly section: "templates/section.html";
19
+ };
20
+ export declare function createTemplate(name: string, dest: string, type?: keyof typeof ASSET_PATHS, options?: {
21
+ allowExisting: boolean;
22
+ }): Promise<void>;
23
+ export declare const TEMPLATE_TYPES: {
24
+ unmapped: number;
25
+ email_base_template: number;
26
+ email: number;
27
+ landing_page_base_template: number;
28
+ landing_page: number;
29
+ blog_base: number;
30
+ blog: number;
31
+ blog_listing: number;
32
+ site_page: number;
33
+ blog_listing_context: number;
34
+ blog_post_context: number;
35
+ error_page: number;
36
+ subscription_preferences: number;
37
+ unsubscribe_confirmation: number;
38
+ unsubscribe_simple: number;
39
+ optin_email: number;
40
+ optin_followup_email: number;
41
+ optin_confirmation_page: number;
42
+ global_group: number;
43
+ password_prompt_page: number;
44
+ resubscribe_email: number;
45
+ unsubscribe_confirmation_email: number;
46
+ resubscribe_confirmation_email: number;
47
+ custom_module: number;
48
+ css: number;
49
+ js: number;
50
+ search_results: number;
51
+ membership_login: number;
52
+ membership_register: number;
53
+ membership_reset: number;
54
+ membership_reset_request: number;
55
+ drag_drop_email: number;
56
+ knowledge_article: number;
57
+ membership_email: number;
58
+ section: number;
59
+ global_content_partial: number;
60
+ simple_landing_page_template: number;
61
+ proposal: number;
62
+ blog_post: number;
63
+ quote: number;
64
+ };
65
+ export {};
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TEMPLATE_TYPES = exports.createTemplate = exports.isCodedFile = exports.getAnnotationValue = exports.ANNOTATION_KEYS = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const github_1 = require("../github");
10
+ const logger_1 = require("../logger");
11
+ const lang_1 = require("../../utils/lang");
12
+ const i18nKey = 'lib.cms.templates';
13
+ // Matches the .html file extension, excluding module.html
14
+ const TEMPLATE_EXTENSION_REGEX = new RegExp(/(?<!module)\.html$/);
15
+ // Matches an annotation value, ending at space, newline, or end of string
16
+ const ANNOTATION_VALUE_REGEX = ':\\s?([\\S|\\s]*?)(\n|$)';
17
+ exports.ANNOTATION_KEYS = {
18
+ isAvailableForNewContent: 'isAvailableForNewContent',
19
+ templateType: 'templateType',
20
+ label: 'label',
21
+ screenshotPath: 'screenshotPath',
22
+ // 'description' is specific to Sections
23
+ description: 'description',
24
+ };
25
+ function getAnnotationValue(annotations, key) {
26
+ const valueRegex = new RegExp(`${key}${ANNOTATION_VALUE_REGEX}`);
27
+ const match = annotations.match(valueRegex);
28
+ return match ? match[1].trim() : null;
29
+ }
30
+ exports.getAnnotationValue = getAnnotationValue;
31
+ /*
32
+ * Returns true if:
33
+ * .html extension (ignoring module.html)
34
+ */
35
+ function isCodedFile(filePath) {
36
+ return TEMPLATE_EXTENSION_REGEX.test(filePath);
37
+ }
38
+ exports.isCodedFile = isCodedFile;
39
+ const ASSET_PATHS = {
40
+ 'page-template': 'templates/page-template.html',
41
+ partial: 'templates/partial.html',
42
+ 'global-partial': 'templates/global-partial.html',
43
+ 'email-template': 'templates/email-template.html',
44
+ 'blog-listing-template': 'templates/blog-listing-template.html',
45
+ 'blog-post-template': 'templates/blog-post-template.html',
46
+ 'search-template': 'templates/search-template.html',
47
+ section: 'templates/section.html',
48
+ };
49
+ async function createTemplate(name, dest, type = 'page-template', options = { allowExisting: false }) {
50
+ const assetPath = ASSET_PATHS[type];
51
+ const filename = name.endsWith('.html') ? name : `${name}.html`;
52
+ const filePath = path_1.default.join(dest, filename);
53
+ if (!options.allowExisting && fs_extra_1.default.existsSync(filePath)) {
54
+ throw new Error((0, lang_1.i18n)(`${i18nKey}.createTemplate.errors.pathExists`, {
55
+ path: filePath,
56
+ }));
57
+ }
58
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.createTemplate.creatingPath`, { path: dest }));
59
+ fs_extra_1.default.mkdirp(dest);
60
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createTemplate.creatingFile`, {
61
+ path: filePath,
62
+ }));
63
+ await (0, github_1.downloadGithubRepoContents)('HubSpot/cms-sample-assets', assetPath, filePath);
64
+ }
65
+ exports.createTemplate = createTemplate;
66
+ exports.TEMPLATE_TYPES = {
67
+ unmapped: 0,
68
+ email_base_template: 1,
69
+ email: 2,
70
+ landing_page_base_template: 3,
71
+ landing_page: 4,
72
+ blog_base: 5,
73
+ blog: 6,
74
+ blog_listing: 42,
75
+ site_page: 8,
76
+ blog_listing_context: 9,
77
+ blog_post_context: 10,
78
+ error_page: 11,
79
+ subscription_preferences: 12,
80
+ unsubscribe_confirmation: 13,
81
+ unsubscribe_simple: 14,
82
+ optin_email: 15,
83
+ optin_followup_email: 16,
84
+ optin_confirmation_page: 17,
85
+ global_group: 18,
86
+ password_prompt_page: 19,
87
+ resubscribe_email: 20,
88
+ unsubscribe_confirmation_email: 21,
89
+ resubscribe_confirmation_email: 22,
90
+ custom_module: 23,
91
+ css: 24,
92
+ js: 25,
93
+ search_results: 27,
94
+ membership_login: 29,
95
+ membership_register: 30,
96
+ membership_reset: 31,
97
+ membership_reset_request: 32,
98
+ drag_drop_email: 34,
99
+ knowledge_article: 35,
100
+ membership_email: 36,
101
+ section: 37,
102
+ global_content_partial: 38,
103
+ simple_landing_page_template: 39,
104
+ proposal: 40,
105
+ blog_post: 41,
106
+ quote: 43,
107
+ };
@@ -0,0 +1,2 @@
1
+ export declare function getThemeJSONPath(path: string): string | null;
2
+ export declare function getThemePreviewUrl(filePath: string, accountId: number): string | undefined;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getThemePreviewUrl = exports.getThemeJSONPath = void 0;
7
+ const findup_sync_1 = __importDefault(require("findup-sync"));
8
+ const urls_1 = require("../urls");
9
+ const environments_1 = require("../../constants/environments");
10
+ const config_1 = require("../../config");
11
+ function getThemeJSONPath(path) {
12
+ return (0, findup_sync_1.default)('theme.json', {
13
+ cwd: path,
14
+ nocase: true,
15
+ });
16
+ }
17
+ exports.getThemeJSONPath = getThemeJSONPath;
18
+ function getThemeNameFromPath(filePath) {
19
+ const themeJSONPath = getThemeJSONPath(filePath);
20
+ if (!themeJSONPath)
21
+ return;
22
+ const pathParts = themeJSONPath.split('/');
23
+ if (pathParts.length < 2)
24
+ return;
25
+ return pathParts[pathParts.length - 2];
26
+ }
27
+ function getThemePreviewUrl(filePath, accountId) {
28
+ const themeName = getThemeNameFromPath(filePath);
29
+ if (!themeName)
30
+ return;
31
+ const baseUrl = (0, urls_1.getHubSpotWebsiteOrigin)((0, config_1.getEnv)(accountId) === 'qa' ? environments_1.ENVIRONMENTS.QA : environments_1.ENVIRONMENTS.PROD);
32
+ return `${baseUrl}/theme-previewer/${accountId}/edit/${encodeURIComponent(themeName)}`;
33
+ }
34
+ exports.getThemePreviewUrl = getThemePreviewUrl;
@@ -0,0 +1,7 @@
1
+ import { FieldsJs } from './handleFieldsJS';
2
+ import { FileMapperInputOptions } from '../../types/Files';
3
+ import { UploadFolderResults, CommandOptions, FilePathsByType } from '../../types/Files';
4
+ import { Mode } from '../../types/Files';
5
+ export declare function getFilesByType(filePaths: Array<string>, projectDir: string, rootWriteDir: string | null, commandOptions: CommandOptions): Promise<[FilePathsByType, Array<FieldsJs>]>;
6
+ export declare function uploadFolder(accountId: number, src: string, dest: string, fileMapperOptions: FileMapperInputOptions, commandOptions?: CommandOptions, filePaths?: Array<string>, mode?: Mode | null): Promise<Array<UploadFolderResults>>;
7
+ export declare function hasUploadErrors(results: Array<UploadFolderResults>): boolean;
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.hasUploadErrors = exports.uploadFolder = exports.getFilesByType = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const p_queue_1 = __importDefault(require("p-queue"));
9
+ const handleFieldsJS_1 = require("./handleFieldsJS");
10
+ const fileMapper_1 = require("../fileMapper");
11
+ const fileMapper_2 = require("../../api/fileMapper");
12
+ const modules_1 = require("../../utils/cms/modules");
13
+ const escapeRegExp_1 = require("../escapeRegExp");
14
+ const path_2 = require("../path");
15
+ const errors_1 = require("../../errors");
16
+ const logger_1 = require("../logger");
17
+ const files_1 = require("../../constants/files");
18
+ const lang_1 = require("../../utils/lang");
19
+ const HubSpotHttpError_1 = require("../../models/HubSpotHttpError");
20
+ const i18nKey = 'lib.cms.uploadFolder';
21
+ const queue = new p_queue_1.default({
22
+ concurrency: 10,
23
+ });
24
+ function getFileType(filePath) {
25
+ const extension = (0, path_2.getExt)(filePath);
26
+ const moduleFolder = (0, modules_1.isModuleFolderChild)({ path: filePath, isLocal: true });
27
+ if (moduleFolder)
28
+ return files_1.FILE_TYPES.module;
29
+ switch (extension) {
30
+ case 'js':
31
+ case 'css':
32
+ return files_1.FILE_TYPES.cssAndJs;
33
+ case 'html':
34
+ return files_1.FILE_TYPES.template;
35
+ case 'json':
36
+ return files_1.FILE_TYPES.json;
37
+ default:
38
+ return files_1.FILE_TYPES.other;
39
+ }
40
+ }
41
+ async function getFilesByType(filePaths, projectDir, rootWriteDir, commandOptions) {
42
+ const { convertFields, fieldOptions } = commandOptions;
43
+ const projectDirRegex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(projectDir)}`);
44
+ const fieldsJsObjects = [];
45
+ // Create object with key-value pairs of form { FileType.type: [] }
46
+ const filePathsByType = Object.values(files_1.FILE_TYPES).reduce((acc, fileType) => {
47
+ return {
48
+ ...acc,
49
+ [fileType]: [],
50
+ };
51
+ }, {});
52
+ for (const filePath of filePaths) {
53
+ const fileType = getFileType(filePath);
54
+ const relPath = filePath.replace(projectDirRegex, '');
55
+ if (!convertFields) {
56
+ filePathsByType[fileType].push(filePath);
57
+ continue;
58
+ }
59
+ const convertableFields = (0, handleFieldsJS_1.isConvertableFieldJs)(projectDir, filePath, convertFields);
60
+ if (convertableFields) {
61
+ const rootOrModule = path_1.default.dirname(relPath) === '/' ? files_1.FILE_TYPES.json : files_1.FILE_TYPES.module;
62
+ const fieldsJs = await new handleFieldsJS_1.FieldsJs(projectDir, filePath, rootWriteDir, fieldOptions).init();
63
+ /*
64
+ * A fields.js will be rejected if the promise is rejected or if the some other error occurs.
65
+ * We handle this gracefully by not adding the failed fields.js to the object list.
66
+ */
67
+ if (fieldsJs.rejected)
68
+ continue;
69
+ fieldsJsObjects.push(fieldsJs);
70
+ filePathsByType[rootOrModule].push(fieldsJs.outputPath || '');
71
+ }
72
+ else {
73
+ filePathsByType[fileType].push(filePath);
74
+ }
75
+ }
76
+ return [filePathsByType, fieldsJsObjects];
77
+ }
78
+ exports.getFilesByType = getFilesByType;
79
+ const defaultUploadAttemptCallback = (file, destPath) => logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.uploadFolder.attempt`, {
80
+ file: file || '',
81
+ destPath,
82
+ }));
83
+ const defaultUploadSuccessCallback = (file, destPath) => logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.uploadFolder.success`, {
84
+ file: file || '',
85
+ destPath,
86
+ }));
87
+ const defaultUploadFirstErrorCallback = (file, destPath, error) => {
88
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.uploadFolder.failed`, { file, destPath }));
89
+ if ((0, errors_1.isHubSpotHttpError)(error)) {
90
+ logger_1.logger.debug(error.data);
91
+ }
92
+ else if (error instanceof Error) {
93
+ logger_1.logger.debug(error.message);
94
+ }
95
+ };
96
+ const defaultUploadRetryCallback = (file, destPath) => logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.uploadFolder.retry`, { file, destPath }));
97
+ const defaultUploadFinalErrorCallback = (accountId, file, destPath, error) => {
98
+ const retryFailed = (0, lang_1.i18n)(`${i18nKey}.uploadFolder.retryFailed`, {
99
+ file,
100
+ destPath,
101
+ });
102
+ logger_1.logger.debug(retryFailed);
103
+ throw new HubSpotHttpError_1.HubSpotHttpError(retryFailed, { cause: error }, {
104
+ accountId,
105
+ request: destPath,
106
+ payload: file,
107
+ });
108
+ };
109
+ async function uploadFolder(accountId, src, dest, fileMapperOptions, commandOptions = {}, filePaths = [], mode = null) {
110
+ const { saveOutput, convertFields, onAttemptCallback, onSuccessCallback, onFirstErrorCallback, onRetryCallback, onFinalErrorCallback, } = commandOptions;
111
+ const _onAttemptCallback = onAttemptCallback || defaultUploadAttemptCallback;
112
+ const _onSuccessCallback = onSuccessCallback || defaultUploadSuccessCallback;
113
+ const _onFirstErrorCallback = onFirstErrorCallback || defaultUploadFirstErrorCallback;
114
+ const _onRetryCallback = onRetryCallback || defaultUploadRetryCallback;
115
+ const _onFinalErrorCallback = onFinalErrorCallback || defaultUploadFinalErrorCallback;
116
+ const tmpDir = convertFields
117
+ ? (0, handleFieldsJS_1.createTmpDirSync)('hubspot-temp-fieldsjs-output-')
118
+ : null;
119
+ const regex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(src)}`);
120
+ const apiOptions = (0, fileMapper_1.getFileMapperQueryValues)(mode, fileMapperOptions);
121
+ const failures = [];
122
+ let fieldsJsPaths = [];
123
+ let tmpDirRegex;
124
+ const [filesByType, fieldsJsObjects] = await getFilesByType(filePaths, src, tmpDir, commandOptions);
125
+ const fileList = Object.values(filesByType);
126
+ if (fieldsJsObjects.length) {
127
+ fieldsJsPaths = fieldsJsObjects.map(fieldsJs => {
128
+ return { outputPath: fieldsJs.outputPath, filePath: fieldsJs.filePath };
129
+ });
130
+ tmpDirRegex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(tmpDir || '')}`);
131
+ }
132
+ function uploadFile(file) {
133
+ const fieldsJsFileInfo = fieldsJsPaths.find(f => f.outputPath === file);
134
+ const originalFilePath = fieldsJsFileInfo
135
+ ? fieldsJsFileInfo.filePath
136
+ : file;
137
+ // files in fieldsJsPaths always belong to the tmp directory.
138
+ const relativePath = file.replace(fieldsJsFileInfo ? tmpDirRegex : regex, '');
139
+ const destPath = (0, path_2.convertToUnixPath)(path_1.default.join(dest, relativePath));
140
+ return async () => {
141
+ _onAttemptCallback(originalFilePath, destPath);
142
+ try {
143
+ await (0, fileMapper_2.upload)(accountId, file, destPath, apiOptions);
144
+ _onSuccessCallback(originalFilePath, destPath);
145
+ }
146
+ catch (err) {
147
+ if ((0, errors_1.isAuthError)(err)) {
148
+ throw err;
149
+ }
150
+ _onFirstErrorCallback(file, destPath, err);
151
+ failures.push({
152
+ file,
153
+ destPath,
154
+ });
155
+ }
156
+ };
157
+ }
158
+ for (let i = 0; i < fileList.length; i++) {
159
+ const filesToUpload = fileList[i];
160
+ await queue.addAll(filesToUpload.map(uploadFile));
161
+ }
162
+ const results = await queue
163
+ .addAll(failures.map(({ file, destPath }) => {
164
+ return async () => {
165
+ _onRetryCallback(file, destPath);
166
+ try {
167
+ await (0, fileMapper_2.upload)(accountId, file, destPath, apiOptions);
168
+ _onSuccessCallback(file, destPath);
169
+ return {
170
+ resultType: files_1.FILE_UPLOAD_RESULT_TYPES.SUCCESS,
171
+ error: null,
172
+ file,
173
+ };
174
+ }
175
+ catch (error) {
176
+ if ((0, errors_1.isAuthError)(error)) {
177
+ throw error;
178
+ }
179
+ _onFinalErrorCallback(accountId, file, destPath, error);
180
+ return {
181
+ resultType: files_1.FILE_UPLOAD_RESULT_TYPES.FAILURE,
182
+ error,
183
+ file,
184
+ };
185
+ }
186
+ };
187
+ }))
188
+ .finally(() => {
189
+ if (!convertFields)
190
+ return;
191
+ if (saveOutput) {
192
+ fieldsJsObjects.forEach(fieldsJs => fieldsJs.saveOutput());
193
+ }
194
+ (0, handleFieldsJS_1.cleanupTmpDirSync)(tmpDir || '');
195
+ });
196
+ return results;
197
+ }
198
+ exports.uploadFolder = uploadFolder;
199
+ function hasUploadErrors(results) {
200
+ return results.some(result => result.resultType === files_1.FILE_UPLOAD_RESULT_TYPES.FAILURE);
201
+ }
202
+ exports.hasUploadErrors = hasUploadErrors;
@@ -0,0 +1,2 @@
1
+ import { LintResult } from '../../types/HublValidation';
2
+ export declare function lint(accountId: number, filepath: string, callback?: (lintResult: LintResult) => number): Promise<Array<Partial<LintResult>> | void>;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.lint = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const extensions_1 = require("../../constants/extensions");
9
+ const validateHubl_1 = require("../../api/validateHubl");
10
+ const fs_1 = require("../fs");
11
+ const path_1 = require("../path");
12
+ async function lint(accountId, filepath, callback) {
13
+ const stats = await fs_extra_1.default.stat(filepath);
14
+ const files = stats.isDirectory() ? await (0, fs_1.walk)(filepath) : [filepath];
15
+ if (!(files && files.length)) {
16
+ return [];
17
+ }
18
+ return Promise.all(files
19
+ .filter(file => extensions_1.HUBL_EXTENSIONS.has((0, path_1.getExt)(file)))
20
+ .map(async (file) => {
21
+ const source = await fs_extra_1.default.readFile(file, { encoding: 'utf8' });
22
+ if (!(source && source.trim())) {
23
+ const result = { file, validation: null };
24
+ if (callback) {
25
+ callback(result);
26
+ }
27
+ return result;
28
+ }
29
+ const { data: validation } = await (0, validateHubl_1.validateHubl)(accountId, source);
30
+ const result = {
31
+ file,
32
+ validation,
33
+ };
34
+ if (callback) {
35
+ callback(result);
36
+ }
37
+ return result;
38
+ }));
39
+ }
40
+ exports.lint = lint;
@@ -0,0 +1,4 @@
1
+ import chokidar from 'chokidar';
2
+ import { WatchOptions, WatchErrorHandler } from '../../types/Files';
3
+ import { UploadFolderResults } from '../../types/Files';
4
+ export declare function watch(accountId: number, src: string, dest: string, { mode, remove, disableInitial, notify, commandOptions, filePaths, }: WatchOptions, postInitialUploadCallback?: ((result: Array<UploadFolderResults>) => void) | null, onUploadFolderError?: WatchErrorHandler, onQueueAddError?: WatchErrorHandler, onUploadFileError?: (file: string, dest: string, accountId: number) => WatchErrorHandler): chokidar.FSWatcher;
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.watch = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const chokidar_1 = __importDefault(require("chokidar"));
9
+ const p_queue_1 = __importDefault(require("p-queue"));
10
+ const debounce_1 = __importDefault(require("debounce"));
11
+ const handleFieldsJS_1 = require("./handleFieldsJS");
12
+ const uploadFolder_1 = require("./uploadFolder");
13
+ const ignoreRules_1 = require("../ignoreRules");
14
+ const fileMapper_1 = require("../fileMapper");
15
+ const fileMapper_2 = require("../../api/fileMapper");
16
+ const escapeRegExp_1 = require("../escapeRegExp");
17
+ const path_2 = require("../path");
18
+ const notify_1 = require("../notify");
19
+ const themes_1 = require("./themes");
20
+ const logger_1 = require("../logger");
21
+ const lang_1 = require("../../utils/lang");
22
+ const HubSpotHttpError_1 = require("../../models/HubSpotHttpError");
23
+ const errors_1 = require("../../errors");
24
+ const i18nKey = 'lib.cms.watch';
25
+ const queue = new p_queue_1.default({
26
+ concurrency: 10,
27
+ });
28
+ function _notifyOfThemePreview(filePath, accountId) {
29
+ if (queue.size > 0)
30
+ return;
31
+ const previewUrl = (0, themes_1.getThemePreviewUrl)(filePath, accountId);
32
+ if (!previewUrl)
33
+ return;
34
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.notifyOfThemePreview`, {
35
+ previewUrl,
36
+ }));
37
+ }
38
+ const notifyOfThemePreview = (0, debounce_1.default)(_notifyOfThemePreview, 1000);
39
+ const defaultOnUploadFileError = (file, dest, accountId) => (error) => {
40
+ const uploadFailedMessage = (0, lang_1.i18n)(`${i18nKey}.uploadFailed`, {
41
+ file,
42
+ dest,
43
+ });
44
+ logger_1.logger.debug(uploadFailedMessage);
45
+ throw new HubSpotHttpError_1.HubSpotHttpError(uploadFailedMessage, {
46
+ cause: error,
47
+ }, {
48
+ accountId,
49
+ request: dest,
50
+ payload: file,
51
+ });
52
+ };
53
+ async function uploadFile(accountId, file, dest, options, mode = null, onUploadFileError = defaultOnUploadFileError) {
54
+ const src = options.src;
55
+ const absoluteSrcPath = path_1.default.resolve((0, path_2.getCwd)(), file);
56
+ const themeJsonPath = (0, themes_1.getThemeJSONPath)(absoluteSrcPath);
57
+ const projectRoot = themeJsonPath
58
+ ? path_1.default.dirname(themeJsonPath)
59
+ : path_1.default.dirname((0, path_2.getCwd)());
60
+ const convertFields = (0, handleFieldsJS_1.isConvertableFieldJs)(src, file, options.commandOptions.convertFields);
61
+ if (!(0, path_2.isAllowedExtension)(file) && !convertFields) {
62
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.skipUnsupportedExtension`, { file }));
63
+ return;
64
+ }
65
+ if ((0, ignoreRules_1.shouldIgnoreFile)(file)) {
66
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.skipIgnoreRule`, { file }));
67
+ return;
68
+ }
69
+ let fieldsJs = undefined;
70
+ if (convertFields) {
71
+ fieldsJs = await new handleFieldsJS_1.FieldsJs(projectRoot, absoluteSrcPath, undefined, options.fieldOptions).init();
72
+ if (fieldsJs.rejected)
73
+ return;
74
+ // Ensures that the dest path is a .json. The user might pass '.js' accidentally - this ensures it just works.
75
+ dest = (0, path_2.convertToUnixPath)(path_1.default.join(path_1.default.dirname(dest), 'fields.json'));
76
+ }
77
+ const fileToUpload = convertFields && fieldsJs?.outputPath ? fieldsJs.outputPath : file;
78
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.uploadAttempt`, { file, dest }));
79
+ const apiOptions = (0, fileMapper_1.getFileMapperQueryValues)(mode, options);
80
+ queue.add(() => {
81
+ return (0, fileMapper_2.upload)(accountId, fileToUpload, dest, apiOptions)
82
+ .then(() => {
83
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.uploadSuccess`, { file, dest }));
84
+ notifyOfThemePreview(file, accountId);
85
+ })
86
+ .catch(() => {
87
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.uploadFailed`, { file, dest }));
88
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.uploadRetry`, { file, dest }));
89
+ return (0, fileMapper_2.upload)(accountId, file, dest, apiOptions).catch(onUploadFileError(file, dest, accountId));
90
+ });
91
+ });
92
+ }
93
+ async function deleteRemoteFile(accountId, filePath, remoteFilePath) {
94
+ if ((0, ignoreRules_1.shouldIgnoreFile)(filePath)) {
95
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.skipIgnoreRule`, { file: filePath }));
96
+ return;
97
+ }
98
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.deleteAttempt`, { remoteFilePath }));
99
+ return queue.add(() => {
100
+ return (0, fileMapper_2.deleteFile)(accountId, remoteFilePath)
101
+ .then(() => {
102
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.deleteSuccess`, { remoteFilePath }));
103
+ notifyOfThemePreview(filePath, accountId);
104
+ })
105
+ .catch(error => {
106
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.deleteFailed`, {
107
+ remoteFilePath,
108
+ }));
109
+ if ((0, errors_1.isHubSpotHttpError)(error)) {
110
+ error.updateContext({
111
+ accountId,
112
+ request: remoteFilePath,
113
+ });
114
+ }
115
+ throw error;
116
+ });
117
+ });
118
+ }
119
+ function watch(accountId, src, dest, { mode, remove, disableInitial, notify, commandOptions, filePaths, }, postInitialUploadCallback = null, onUploadFolderError, onQueueAddError, onUploadFileError) {
120
+ const regex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(src)}`);
121
+ if (notify) {
122
+ (0, ignoreRules_1.ignoreFile)(notify);
123
+ }
124
+ const watcher = chokidar_1.default.watch(src, {
125
+ ignoreInitial: true,
126
+ ignored: (file) => (0, ignoreRules_1.shouldIgnoreFile)(file),
127
+ });
128
+ function getDesignManagerPath(file) {
129
+ const relativePath = file.replace(regex, '');
130
+ return (0, path_2.convertToUnixPath)(path_1.default.join(dest, relativePath));
131
+ }
132
+ if (!disableInitial) {
133
+ // Use uploadFolder so that failures of initial upload are retried
134
+ const uploadFolderPromise = (0, uploadFolder_1.uploadFolder)(accountId, src, dest, {}, commandOptions, filePaths, mode || null).then(result => {
135
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.folderUploadSuccess`, {
136
+ src,
137
+ dest,
138
+ accountId,
139
+ }));
140
+ if (postInitialUploadCallback) {
141
+ postInitialUploadCallback(result);
142
+ }
143
+ });
144
+ if (onUploadFolderError) {
145
+ uploadFolderPromise.catch(onUploadFolderError);
146
+ }
147
+ }
148
+ watcher.on('ready', () => {
149
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.ready`, { src }));
150
+ });
151
+ watcher.on('add', async (filePath) => {
152
+ const destPath = getDesignManagerPath(filePath);
153
+ const uploadPromise = uploadFile(accountId, filePath, destPath, {
154
+ src,
155
+ commandOptions,
156
+ }, mode, onUploadFileError);
157
+ (0, notify_1.triggerNotify)(notify, 'Added', filePath, uploadPromise);
158
+ });
159
+ if (remove) {
160
+ const deleteFileOrFolder = (type) => (filePath) => {
161
+ // If it's a fields.js file that is in a module folder or the root, then ignore because it will not exist on the server.
162
+ if ((0, handleFieldsJS_1.isConvertableFieldJs)(src, filePath, commandOptions.convertFields)) {
163
+ return;
164
+ }
165
+ const remotePath = getDesignManagerPath(filePath);
166
+ if ((0, ignoreRules_1.shouldIgnoreFile)(filePath)) {
167
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.skipIgnoreRule`, { file: filePath }));
168
+ return;
169
+ }
170
+ logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.deleteAttemptWithType`, {
171
+ type,
172
+ remoteFilePath: remotePath,
173
+ }));
174
+ const queueAddPromise = queue.add(() => {
175
+ const deletePromise = deleteRemoteFile(accountId, filePath, remotePath).then(() => {
176
+ logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.deleteSuccessWithType`, {
177
+ type,
178
+ remoteFilePath: remotePath,
179
+ }));
180
+ });
181
+ if (onQueueAddError) {
182
+ queueAddPromise.catch(onQueueAddError);
183
+ }
184
+ (0, notify_1.triggerNotify)(notify, 'Removed', filePath, deletePromise);
185
+ return deletePromise;
186
+ });
187
+ };
188
+ watcher.on('unlink', deleteFileOrFolder('file'));
189
+ watcher.on('unlinkDir', deleteFileOrFolder('folder'));
190
+ }
191
+ watcher.on('change', async (filePath) => {
192
+ const destPath = getDesignManagerPath(filePath);
193
+ const uploadPromise = uploadFile(accountId, filePath, destPath, {
194
+ src,
195
+ commandOptions,
196
+ }, mode, onUploadFileError);
197
+ (0, notify_1.triggerNotify)(notify, 'Changed', filePath, uploadPromise);
198
+ });
199
+ return watcher;
200
+ }
201
+ exports.watch = watch;