@hubspot/local-dev-lib 0.0.2 → 0.0.4

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 (87) hide show
  1. package/config/CLIConfiguration.d.ts +55 -0
  2. package/config/CLIConfiguration.js +392 -0
  3. package/config/configFile.d.ts +21 -0
  4. package/config/configFile.js +109 -0
  5. package/config/configUtils.d.ts +24 -0
  6. package/config/configUtils.js +85 -0
  7. package/config/environment.d.ts +3 -0
  8. package/config/environment.js +64 -0
  9. package/constants/auth.d.ts +24 -0
  10. package/constants/auth.js +31 -0
  11. package/constants/config.d.ts +8 -0
  12. package/constants/config.js +12 -0
  13. package/constants/environments.d.ts +13 -0
  14. package/constants/environments.js +16 -0
  15. package/constants/extensions.d.ts +2 -0
  16. package/constants/extensions.js +23 -0
  17. package/constants/files.d.ts +5 -0
  18. package/constants/files.js +8 -0
  19. package/constants/github.d.ts +4 -0
  20. package/constants/github.js +7 -0
  21. package/constants/index.d.ts +16 -0
  22. package/constants/index.js +12 -0
  23. package/errors/HubSpotAuthError.d.ts +3 -0
  24. package/errors/HubSpotAuthError.js +6 -0
  25. package/errors/fileSystemErrors.d.ts +8 -0
  26. package/errors/fileSystemErrors.js +28 -0
  27. package/errors/standardErrors.d.ts +19 -0
  28. package/errors/standardErrors.js +67 -0
  29. package/http/requestOptions.d.ts +20 -0
  30. package/http/requestOptions.js +27 -0
  31. package/lib/archive.d.ts +7 -0
  32. package/lib/archive.js +111 -0
  33. package/lib/cms/handleFieldsJS.d.ts +32 -0
  34. package/lib/cms/handleFieldsJS.js +143 -0
  35. package/lib/cms/index.d.ts +10 -0
  36. package/lib/cms/index.js +13 -0
  37. package/lib/cms/modules.d.ts +24 -0
  38. package/lib/cms/modules.js +124 -0
  39. package/lib/cms/themes.d.ts +2 -0
  40. package/lib/cms/themes.js +34 -0
  41. package/lib/environment.d.ts +1 -0
  42. package/lib/environment.js +16 -0
  43. package/lib/fs.d.ts +4 -0
  44. package/lib/fs.js +71 -0
  45. package/lib/github.d.ts +17 -0
  46. package/lib/github.js +133 -0
  47. package/lib/gitignore.d.ts +1 -0
  48. package/lib/gitignore.js +76 -0
  49. package/lib/index.d.ts +11 -0
  50. package/lib/index.js +14 -0
  51. package/lib/path.d.ts +10 -0
  52. package/lib/path.js +84 -0
  53. package/lib/text.d.ts +1 -0
  54. package/lib/text.js +16 -0
  55. package/lib/urls.d.ts +2 -0
  56. package/lib/urls.js +20 -0
  57. package/package.json +8 -11
  58. package/types/Accounts.d.ts +50 -0
  59. package/types/Accounts.js +2 -0
  60. package/types/CLIOptions.d.ts +3 -0
  61. package/types/CLIOptions.js +2 -0
  62. package/types/Config.d.ts +10 -0
  63. package/types/Config.js +2 -0
  64. package/types/Error.d.ts +31 -0
  65. package/types/Error.js +2 -0
  66. package/types/Files.d.ts +8 -0
  67. package/types/Files.js +2 -0
  68. package/types/Github.d.ts +58 -0
  69. package/types/Github.js +2 -0
  70. package/types/LogCallbacks.d.ts +6 -0
  71. package/types/LogCallbacks.js +2 -0
  72. package/types/Modules.d.ts +5 -0
  73. package/types/Modules.js +2 -0
  74. package/types/Utils.d.ts +1 -0
  75. package/types/Utils.js +2 -0
  76. package/utils/escapeRegExp.d.ts +1 -0
  77. package/utils/escapeRegExp.js +7 -0
  78. package/utils/fieldsJS.d.ts +3 -0
  79. package/utils/fieldsJS.js +18 -0
  80. package/utils/git.d.ts +4 -0
  81. package/utils/git.js +40 -0
  82. package/utils/lang.d.ts +8 -0
  83. package/utils/lang.js +83 -0
  84. package/utils/logger.d.ts +10 -0
  85. package/utils/logger.js +22 -0
  86. package/utils/modules.d.ts +4 -0
  87. package/utils/modules.js +53 -0
@@ -0,0 +1,143 @@
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.cleanupTmpDirSync = exports.createTmpDirSync = exports.isConvertableFieldJs = exports.FieldsJs = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const child_process_1 = require("child_process");
11
+ const escapeRegExp_1 = require("../../utils/escapeRegExp");
12
+ const modules_1 = require("../../utils/modules");
13
+ const logger_1 = require("../../utils/logger");
14
+ const standardErrors_1 = require("../../errors/standardErrors");
15
+ const i18nKey = 'utils.handleFieldsJs';
16
+ class FieldsJs {
17
+ projectDir;
18
+ filePath;
19
+ rootWriteDir;
20
+ rejected;
21
+ fieldOptions;
22
+ outputPath;
23
+ constructor(projectDir, filePath, rootWriteDir, fieldOptions = '') {
24
+ this.projectDir = projectDir;
25
+ this.filePath = filePath;
26
+ this.fieldOptions = fieldOptions;
27
+ this.rejected = false;
28
+ // Create tmpDir if no writeDir is given.
29
+ this.rootWriteDir =
30
+ rootWriteDir === undefined
31
+ ? createTmpDirSync('hubspot-temp-fieldsjs-output-')
32
+ : rootWriteDir;
33
+ }
34
+ async init() {
35
+ const outputPath = await this.getOutputPathPromise();
36
+ this.outputPath = this.rejected ? undefined : outputPath;
37
+ return this;
38
+ }
39
+ // Converts a fields.js file into a fields.json file, writes, and returns of fields.json
40
+ convertFieldsJs(writeDir) {
41
+ const filePath = this.filePath;
42
+ const dirName = path_1.default.dirname(filePath);
43
+ return new Promise((resolve, reject) => {
44
+ const convertFieldsProcess = (0, child_process_1.fork)(path_1.default.join(__dirname, './processFieldsJs.js'), [], {
45
+ cwd: dirName,
46
+ env: {
47
+ dirName,
48
+ fieldOptions: this.fieldOptions,
49
+ filePath,
50
+ writeDir,
51
+ },
52
+ });
53
+ (0, logger_1.debug)(`${i18nKey}.convertFieldsJs.creating`, {
54
+ pid: convertFieldsProcess.pid || '',
55
+ });
56
+ convertFieldsProcess.on('message', function (message) {
57
+ if (message.action === 'ERROR') {
58
+ reject(message.message);
59
+ }
60
+ else if (message.action === 'COMPLETE') {
61
+ resolve(message.finalPath);
62
+ }
63
+ });
64
+ convertFieldsProcess.on('close', () => {
65
+ (0, logger_1.debug)(`${i18nKey}.convertFieldsJs.terminating`, {
66
+ pid: convertFieldsProcess.pid || '',
67
+ });
68
+ });
69
+ }).catch((e) => {
70
+ (0, standardErrors_1.throwErrorWithMessage)(`${i18nKey}.convertFieldsJs`, { filePath }, e);
71
+ });
72
+ }
73
+ /**
74
+ * If there has been a fields.json written to the output path, then copy it from the output
75
+ * directory to the project directory, respecting the path within the output directory.
76
+ * Ex: path/to/tmp/example.module/fields.json => path/to/project/example.module/fields.output.json
77
+ */
78
+ saveOutput() {
79
+ if (!this.outputPath || !fs_extra_1.default.existsSync(this.outputPath)) {
80
+ (0, standardErrors_1.throwErrorWithMessage)(`${i18nKey}.saveOutput`, { path: this.filePath });
81
+ }
82
+ const relativePath = path_1.default.relative(this.rootWriteDir, path_1.default.dirname(this.outputPath));
83
+ const savePath = path_1.default.join(this.projectDir, relativePath, 'fields.output.json');
84
+ try {
85
+ fs_extra_1.default.copyFileSync(this.outputPath, savePath);
86
+ }
87
+ catch (err) {
88
+ (0, standardErrors_1.throwErrorWithMessage)(`${i18nKey}.saveOutput`, { path: savePath }, err);
89
+ }
90
+ }
91
+ /**
92
+ * Resolves the relative path to the fields.js within the project directory and returns
93
+ * directory name to write to in rootWriteDir directory.
94
+ *
95
+ * Ex: If rootWriteDir = 'path/to/temp', filePath = 'projectRoot/sample.module/fields.js'. Then getWriteDir() => path/to/temp/sample.module
96
+ */
97
+ getWriteDir() {
98
+ const projectDirRegex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(this.projectDir)}`);
99
+ const relativePath = this.filePath.replace(projectDirRegex, '');
100
+ return path_1.default.dirname(path_1.default.join(this.rootWriteDir, relativePath));
101
+ }
102
+ getOutputPathPromise() {
103
+ const writeDir = this.getWriteDir();
104
+ return this.convertFieldsJs(writeDir).then(outputPath => outputPath);
105
+ }
106
+ }
107
+ exports.FieldsJs = FieldsJs;
108
+ /**
109
+ * Determines if file is a convertable fields.js file i.e., if it is called
110
+ * 'fields.js' and in a root or in a module folder, and if convertFields flag is true.
111
+ */
112
+ function isConvertableFieldJs(rootDir, filePath, convertFields = false) {
113
+ const allowedFieldsNames = ['fields.js', 'fields.mjs', 'fields.cjs'];
114
+ const regex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(rootDir)}`);
115
+ const relativePath = path_1.default.dirname(filePath.replace(regex, ''));
116
+ const baseName = path_1.default.basename(filePath);
117
+ const inModuleFolder = (0, modules_1.isModuleFolderChild)({ path: filePath, isLocal: true });
118
+ return !!(convertFields &&
119
+ allowedFieldsNames.includes(baseName) &&
120
+ (inModuleFolder || relativePath == '/'));
121
+ }
122
+ exports.isConvertableFieldJs = isConvertableFieldJs;
123
+ // Try creating tempdir
124
+ function createTmpDirSync(prefix) {
125
+ let tmpDir;
126
+ try {
127
+ tmpDir = fs_extra_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), prefix));
128
+ }
129
+ catch (err) {
130
+ (0, standardErrors_1.throwErrorWithMessage)(`${i18nKey}.createTmpDirSync`, {}, err);
131
+ }
132
+ return tmpDir;
133
+ }
134
+ exports.createTmpDirSync = createTmpDirSync;
135
+ // Try cleaning up resources from os's tempdir
136
+ function cleanupTmpDirSync(tmpDir) {
137
+ fs_extra_1.default.rm(tmpDir, { recursive: true }, err => {
138
+ if (err) {
139
+ (0, standardErrors_1.throwErrorWithMessage)(`${i18nKey}.cleanupTmpDirSync`, {}, err);
140
+ }
141
+ });
142
+ }
143
+ exports.cleanupTmpDirSync = cleanupTmpDirSync;
@@ -0,0 +1,10 @@
1
+ import { isConvertableFieldJs as __isConvertableFieldJs, createTmpDirSync as __createTmpDirSync, cleanupTmpDirSync as __cleanupTmpDirSync } from './handleFieldsJS';
2
+ import { validateSrcAndDestPaths as __validateSrcAndDestPaths, createModule as __createModule } from './modules';
3
+ import { getThemeJSONPath as __getThemeJSONPath, getThemePreviewUrl as __getThemePreviewUrl } from './themes';
4
+ export declare const isConvertableFieldJs: typeof __isConvertableFieldJs;
5
+ export declare const createTmpDirSync: typeof __createTmpDirSync;
6
+ export declare const cleanupTmpDirSync: typeof __cleanupTmpDirSync;
7
+ export declare const validateSrcAndDestPaths: typeof __validateSrcAndDestPaths;
8
+ export declare const createModule: typeof __createModule;
9
+ export declare const getThemeJSONPath: typeof __getThemeJSONPath;
10
+ export declare const getThemePreviewUrl: typeof __getThemePreviewUrl;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getThemePreviewUrl = exports.getThemeJSONPath = exports.createModule = exports.validateSrcAndDestPaths = exports.cleanupTmpDirSync = exports.createTmpDirSync = exports.isConvertableFieldJs = void 0;
4
+ const handleFieldsJS_1 = require("./handleFieldsJS");
5
+ const modules_1 = require("./modules");
6
+ const themes_1 = require("./themes");
7
+ exports.isConvertableFieldJs = handleFieldsJS_1.isConvertableFieldJs;
8
+ exports.createTmpDirSync = handleFieldsJS_1.createTmpDirSync;
9
+ exports.cleanupTmpDirSync = handleFieldsJS_1.cleanupTmpDirSync;
10
+ exports.validateSrcAndDestPaths = modules_1.validateSrcAndDestPaths;
11
+ exports.createModule = modules_1.createModule;
12
+ exports.getThemeJSONPath = themes_1.getThemeJSONPath;
13
+ exports.getThemePreviewUrl = themes_1.getThemePreviewUrl;
@@ -0,0 +1,24 @@
1
+ import { LogCallbacksArg } from '../../types/LogCallbacks';
2
+ import { PathInput } from '../../types/Modules';
3
+ export declare const ValidationIds: {
4
+ SRC_REQUIRED: string;
5
+ DEST_REQUIRED: string;
6
+ MODULE_FOLDER_REQUIRED: string;
7
+ MODULE_TO_MODULE_NESTING: string;
8
+ MODULE_NESTING: string;
9
+ };
10
+ type ValidationResult = {
11
+ id: string;
12
+ message: string;
13
+ };
14
+ export declare function validateSrcAndDestPaths(src?: PathInput, dest?: PathInput): Promise<Array<ValidationResult>>;
15
+ type ModuleDefinition = {
16
+ contentTypes: Array<string>;
17
+ moduleLabel: string;
18
+ global: boolean;
19
+ };
20
+ declare const createModuleCallbackKeys: readonly ["creatingPath", "creatingModule"];
21
+ export declare function createModule(moduleDefinition: ModuleDefinition, name: string, dest: string, options?: {
22
+ allowExistingDir: boolean;
23
+ }, logCallbacks?: LogCallbacksArg<typeof createModuleCallbackKeys>): Promise<void>;
24
+ export {};
@@ -0,0 +1,124 @@
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.createModule = exports.validateSrcAndDestPaths = exports.ValidationIds = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_2 = require("../path");
10
+ const fs_1 = require("../fs");
11
+ const github_1 = require("../github");
12
+ const standardErrors_1 = require("../../errors/standardErrors");
13
+ const logger_1 = require("../../utils/logger");
14
+ const modules_1 = require("../../utils/modules");
15
+ // Ids for testing
16
+ exports.ValidationIds = {
17
+ SRC_REQUIRED: 'SRC_REQUIRED',
18
+ DEST_REQUIRED: 'DEST_REQUIRED',
19
+ MODULE_FOLDER_REQUIRED: 'MODULE_FOLDER_REQUIRED',
20
+ MODULE_TO_MODULE_NESTING: 'MODULE_TO_MODULE_NESTING',
21
+ MODULE_NESTING: 'MODULE_NESTING',
22
+ };
23
+ const getValidationResult = (id, message) => ({ id, message });
24
+ async function validateSrcAndDestPaths(src, dest) {
25
+ const results = [];
26
+ if (!(0, modules_1.isPathInput)(src)) {
27
+ results.push(getValidationResult(exports.ValidationIds.SRC_REQUIRED, '`src` is required.'));
28
+ }
29
+ if (!(0, modules_1.isPathInput)(dest)) {
30
+ results.push(getValidationResult(exports.ValidationIds.DEST_REQUIRED, '`dest` is required.'));
31
+ }
32
+ if (results.length || !src || !dest) {
33
+ return results;
34
+ }
35
+ const [_src, _dest] = [src, dest].map(inputPath => {
36
+ const result = { ...inputPath };
37
+ if (result.isLocal) {
38
+ result.path = path_1.default.resolve((0, path_2.getCwd)(), result.path);
39
+ }
40
+ else if (result.isHubSpot) {
41
+ result.path = path_1.default.posix.normalize(result.path);
42
+ }
43
+ return result;
44
+ });
45
+ // src is a .module folder and dest is within a module. (Nesting)
46
+ // e.g. `upload foo.module bar.module/zzz`
47
+ if ((0, modules_1.isModuleFolder)(_src) && (0, modules_1.isModuleFolderChild)(_dest)) {
48
+ return results.concat(getValidationResult(exports.ValidationIds.MODULE_TO_MODULE_NESTING, '`src` is a module path and `dest` is within a module.'));
49
+ }
50
+ // src is a .module folder but dest is not
51
+ // e.g. `upload foo.module bar`
52
+ if ((0, modules_1.isModuleFolder)(_src) && !(0, modules_1.isModuleFolder)(_dest)) {
53
+ return results.concat(getValidationResult(exports.ValidationIds.MODULE_FOLDER_REQUIRED, '`src` is a module path but `dest` is not.'));
54
+ }
55
+ // src is a folder that includes modules and dest is within a module. (Nesting)
56
+ if (_src.isLocal && (0, modules_1.isModuleFolderChild)(_dest)) {
57
+ const stat = await fs_extra_1.default.stat(_src.path);
58
+ if (stat.isDirectory()) {
59
+ const files = await (0, fs_1.walk)(_src.path);
60
+ const srcHasModulesChildren = files.some(file => (0, modules_1.isModuleFolderChild)({ ..._src, path: file }));
61
+ if (srcHasModulesChildren) {
62
+ return results.concat(getValidationResult(exports.ValidationIds.MODULE_NESTING, '`src` contains modules and `dest` is within a module.'));
63
+ }
64
+ }
65
+ }
66
+ return results;
67
+ }
68
+ exports.validateSrcAndDestPaths = validateSrcAndDestPaths;
69
+ const createModuleCallbackKeys = ['creatingPath', 'creatingModule'];
70
+ async function createModule(moduleDefinition, name, dest, options = {
71
+ allowExistingDir: false,
72
+ }, logCallbacks) {
73
+ const logger = (0, logger_1.makeTypedLogger)(logCallbacks, 'modules.createModule');
74
+ const writeModuleMeta = ({ contentTypes, moduleLabel, global }, dest) => {
75
+ const metaData = {
76
+ label: moduleLabel,
77
+ css_assets: [],
78
+ external_js: [],
79
+ global: global,
80
+ help_text: '',
81
+ host_template_types: contentTypes,
82
+ js_assets: [],
83
+ other_assets: [],
84
+ smart_type: 'NOT_SMART',
85
+ tags: [],
86
+ is_available_for_new_content: false,
87
+ };
88
+ fs_extra_1.default.writeJSONSync(dest, metaData, { spaces: 2 });
89
+ };
90
+ const moduleFileFilter = (src, dest) => {
91
+ const emailEnabled = moduleDefinition.contentTypes.includes('EMAIL');
92
+ switch (path_1.default.basename(src)) {
93
+ case 'meta.json':
94
+ writeModuleMeta(moduleDefinition, dest);
95
+ return false;
96
+ case 'module.js':
97
+ case 'module.css':
98
+ if (emailEnabled) {
99
+ return false;
100
+ }
101
+ return true;
102
+ default:
103
+ return true;
104
+ }
105
+ };
106
+ const folderName = !name || name.endsWith('.module') ? name : `${name}.module`;
107
+ const destPath = path_1.default.join(dest, folderName);
108
+ if (!options.allowExistingDir && fs_extra_1.default.existsSync(destPath)) {
109
+ (0, standardErrors_1.throwErrorWithMessage)('modules.createModule', {
110
+ path: destPath,
111
+ });
112
+ }
113
+ else {
114
+ logger('creatingPath', {
115
+ path: destPath,
116
+ });
117
+ fs_extra_1.default.ensureDirSync(destPath);
118
+ }
119
+ logger('creatingModule', {
120
+ path: destPath,
121
+ });
122
+ await (0, github_1.downloadGithubRepoContents)('cms-sample-assets', 'modules/Sample.module', destPath, moduleFileFilter);
123
+ }
124
+ exports.createModule = createModule;
@@ -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 CLIConfiguration_1 = __importDefault(require("../../config/CLIConfiguration"));
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)(CLIConfiguration_1.default.getEnv() === '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 @@
1
+ export declare function getValidEnv(env: string, maskedProductionValue?: string): string;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getValidEnv = void 0;
4
+ const environments_1 = require("../constants/environments");
5
+ function getValidEnv(env, maskedProductionValue) {
6
+ const prodValue = maskedProductionValue
7
+ ? maskedProductionValue
8
+ : environments_1.ENVIRONMENTS.PROD;
9
+ const returnVal = typeof env &&
10
+ typeof env === 'string' &&
11
+ env.toLowerCase() === environments_1.ENVIRONMENTS.QA
12
+ ? environments_1.ENVIRONMENTS.QA
13
+ : prodValue;
14
+ return returnVal;
15
+ }
16
+ exports.getValidEnv = getValidEnv;
package/lib/fs.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { FileData } from '../types/Files';
2
+ export declare function getFileInfoAsync(dir: string, file: string): Promise<FileData>;
3
+ export declare function flattenAndRemoveSymlinks(filesData: Array<FileData>): Array<string>;
4
+ export declare function walk(dir: string): Promise<Array<string>>;
package/lib/fs.js ADDED
@@ -0,0 +1,71 @@
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.walk = exports.flattenAndRemoveSymlinks = exports.getFileInfoAsync = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const files_1 = require("../constants/files");
10
+ const standardErrors_1 = require("../errors/standardErrors");
11
+ function getFileInfoAsync(dir, file) {
12
+ return new Promise((resolve, reject) => {
13
+ const filepath = path_1.default.join(dir, file);
14
+ fs_1.default.lstat(filepath, (error, stats) => {
15
+ if (error) {
16
+ reject(error);
17
+ }
18
+ let type = files_1.STAT_TYPES.FILE;
19
+ if (stats.isSymbolicLink()) {
20
+ type = files_1.STAT_TYPES.SYMBOLIC_LINK;
21
+ }
22
+ else if (stats.isDirectory()) {
23
+ type = files_1.STAT_TYPES.DIRECTORY;
24
+ }
25
+ resolve({ filepath, type });
26
+ });
27
+ });
28
+ }
29
+ exports.getFileInfoAsync = getFileInfoAsync;
30
+ function flattenAndRemoveSymlinks(filesData) {
31
+ return filesData.reduce((acc, fileData) => {
32
+ switch (fileData.type) {
33
+ case files_1.STAT_TYPES.FILE:
34
+ return acc.concat(fileData.filepath);
35
+ case files_1.STAT_TYPES.DIRECTORY:
36
+ return acc.concat(fileData.files || []);
37
+ case files_1.STAT_TYPES.SYMBOLIC_LINK:
38
+ return acc;
39
+ default:
40
+ return acc;
41
+ }
42
+ }, []);
43
+ }
44
+ exports.flattenAndRemoveSymlinks = flattenAndRemoveSymlinks;
45
+ const generateRecursiveFilePromise = async (dir, file) => {
46
+ return getFileInfoAsync(dir, file).then(fileData => {
47
+ return new Promise(resolve => {
48
+ if (fileData.type === files_1.STAT_TYPES.DIRECTORY) {
49
+ walk(fileData.filepath).then(files => {
50
+ resolve({ ...fileData, files });
51
+ });
52
+ }
53
+ else {
54
+ resolve(fileData);
55
+ }
56
+ });
57
+ });
58
+ };
59
+ async function walk(dir) {
60
+ function processFiles(files) {
61
+ return Promise.all(files.map(file => generateRecursiveFilePromise(dir, file)));
62
+ }
63
+ return fs_1.default.promises
64
+ .readdir(dir)
65
+ .then(processFiles)
66
+ .then(flattenAndRemoveSymlinks)
67
+ .catch(err => {
68
+ (0, standardErrors_1.throwError)(err);
69
+ });
70
+ }
71
+ exports.walk = walk;
@@ -0,0 +1,17 @@
1
+ import { GITHUB_RELEASE_TYPES } from '../constants/github';
2
+ import { ValueOf } from '../types/Utils';
3
+ import { LogCallbacksArg } from '../types/LogCallbacks';
4
+ declare global {
5
+ var githubToken: string;
6
+ }
7
+ export declare function fetchJsonFromRepository(repoName: string, filePath: string): Promise<JSON>;
8
+ type CloneGithubRepoOptions = {
9
+ themeVersion?: string;
10
+ projectVersion?: string;
11
+ releaseType?: ValueOf<typeof GITHUB_RELEASE_TYPES>;
12
+ ref?: string;
13
+ };
14
+ declare const cloneGithubRepoCallbackKeys: string[];
15
+ export declare function cloneGithubRepo(dest: string, type: string, repoName: string, sourceDir: string, options?: CloneGithubRepoOptions, logCallbacks?: LogCallbacksArg<typeof cloneGithubRepoCallbackKeys>): Promise<boolean>;
16
+ export declare function downloadGithubRepoContents(repoName: string, contentPath: string, dest: string, filter?: (contentPiecePath: string, downloadPath: string) => boolean): Promise<void>;
17
+ export {};
package/lib/github.js ADDED
@@ -0,0 +1,133 @@
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.downloadGithubRepoContents = exports.cloneGithubRepo = exports.fetchJsonFromRepository = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const logger_1 = require("../utils/logger");
11
+ const standardErrors_1 = require("../errors/standardErrors");
12
+ const archive_1 = require("./archive");
13
+ const github_1 = require("../constants/github");
14
+ const requestOptions_1 = require("../http/requestOptions");
15
+ const GITHUB_AUTH_HEADERS = {
16
+ authorization: global && global.githubToken ? `Bearer ${global.githubToken}` : null,
17
+ };
18
+ async function fetchJsonFromRepository(repoName, filePath) {
19
+ try {
20
+ const URI = `https://raw.githubusercontent.com/HubSpot/${repoName}/${filePath}`;
21
+ (0, logger_1.debug)('github.fetchJsonFromRepository', { uri: URI });
22
+ const { data } = await axios_1.default.get(URI, {
23
+ headers: { ...requestOptions_1.DEFAULT_USER_AGENT_HEADERS, ...GITHUB_AUTH_HEADERS },
24
+ });
25
+ return data;
26
+ }
27
+ catch (err) {
28
+ (0, standardErrors_1.throwErrorWithMessage)('github.fetchJsonFromRepository', {}, err);
29
+ }
30
+ }
31
+ exports.fetchJsonFromRepository = fetchJsonFromRepository;
32
+ async function fetchReleaseData(repoName, tag = '') {
33
+ tag = tag.trim().toLowerCase();
34
+ if (tag.length && tag[0] !== 'v') {
35
+ tag = `v${tag}`;
36
+ }
37
+ const URI = tag
38
+ ? `https://api.github.com/repos/HubSpot/${repoName}/releases/tags/${tag}`
39
+ : `https://api.github.com/repos/HubSpot/${repoName}/releases/latest`;
40
+ try {
41
+ const { data } = await axios_1.default.get(URI, {
42
+ headers: { ...requestOptions_1.DEFAULT_USER_AGENT_HEADERS, ...GITHUB_AUTH_HEADERS },
43
+ });
44
+ return data;
45
+ }
46
+ catch (err) {
47
+ const error = err;
48
+ (0, standardErrors_1.throwErrorWithMessage)('github.fetchReleaseData', { tag: tag || 'latest' }, error);
49
+ }
50
+ }
51
+ async function downloadGithubRepoZip(repoName, tag = '', releaseType = github_1.GITHUB_RELEASE_TYPES.RELEASE, ref) {
52
+ try {
53
+ let zipUrl;
54
+ if (releaseType === github_1.GITHUB_RELEASE_TYPES.REPOSITORY) {
55
+ (0, logger_1.debug)('github.downloadGithubRepoZip.fetching', { releaseType, repoName });
56
+ zipUrl = `https://api.github.com/repos/HubSpot/${repoName}/zipball${ref ? `/${ref}` : ''}`;
57
+ }
58
+ else {
59
+ const releaseData = await fetchReleaseData(repoName, tag);
60
+ zipUrl = releaseData.zipball_url;
61
+ const { name } = releaseData;
62
+ (0, logger_1.debug)('github.downloadGithubRepoZip.fetchingName', { name });
63
+ }
64
+ const { data } = await axios_1.default.get(zipUrl, {
65
+ headers: { ...requestOptions_1.DEFAULT_USER_AGENT_HEADERS, ...GITHUB_AUTH_HEADERS },
66
+ });
67
+ (0, logger_1.debug)('github.downloadGithubRepoZip.completed');
68
+ return data;
69
+ }
70
+ catch (err) {
71
+ (0, standardErrors_1.throwErrorWithMessage)('github.downloadGithubRepoZip', {}, err);
72
+ }
73
+ }
74
+ const cloneGithubRepoCallbackKeys = ['success'];
75
+ async function cloneGithubRepo(dest, type, repoName, sourceDir, options = {}, logCallbacks) {
76
+ const logger = (0, logger_1.makeTypedLogger)(logCallbacks, 'github.cloneGithubRepo');
77
+ const { themeVersion, projectVersion, releaseType, ref } = options;
78
+ const tag = projectVersion || themeVersion;
79
+ const zip = await downloadGithubRepoZip(repoName, tag, releaseType, ref);
80
+ const success = await (0, archive_1.extractZipArchive)(zip, repoName, dest, { sourceDir });
81
+ if (success) {
82
+ logger('success', { type, dest });
83
+ }
84
+ return success;
85
+ }
86
+ exports.cloneGithubRepo = cloneGithubRepo;
87
+ async function getGitHubRepoContentsAtPath(repoName, path) {
88
+ const contentsRequestUrl = `https://api.github.com/repos/HubSpot/${repoName}/contents/${path}`;
89
+ const response = await axios_1.default.get(contentsRequestUrl, {
90
+ headers: { ...requestOptions_1.DEFAULT_USER_AGENT_HEADERS, ...GITHUB_AUTH_HEADERS },
91
+ });
92
+ return response.data;
93
+ }
94
+ async function fetchGitHubRepoContentFromDownloadUrl(dest, downloadUrl) {
95
+ const resp = await axios_1.default.get(downloadUrl, {
96
+ headers: { ...requestOptions_1.DEFAULT_USER_AGENT_HEADERS, ...GITHUB_AUTH_HEADERS },
97
+ });
98
+ fs_extra_1.default.writeFileSync(dest, resp.data, 'utf8');
99
+ }
100
+ // Writes files from a HubSpot public repository to the destination folder
101
+ async function downloadGithubRepoContents(repoName, contentPath, dest, filter) {
102
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(dest));
103
+ try {
104
+ const contentsResp = await getGitHubRepoContentsAtPath(repoName, contentPath);
105
+ const downloadContent = async (contentPiece) => {
106
+ const { path: contentPiecePath, download_url } = contentPiece;
107
+ const downloadPath = path_1.default.join(dest, contentPiecePath.replace(contentPath, ''));
108
+ if (filter && !filter(contentPiecePath, downloadPath)) {
109
+ return Promise.resolve();
110
+ }
111
+ (0, logger_1.debug)('github.downloadGithubRepoContents.downloading', {
112
+ contentPiecePath,
113
+ downloadUrl: download_url,
114
+ downloadPath,
115
+ });
116
+ return fetchGitHubRepoContentFromDownloadUrl(downloadPath, download_url);
117
+ };
118
+ const contentPromises = contentsResp.map(downloadContent);
119
+ Promise.all(contentPromises);
120
+ }
121
+ catch (e) {
122
+ const error = e;
123
+ if (error.error.message) {
124
+ (0, standardErrors_1.throwErrorWithMessage)('github.downloadGithubRepoContents', {
125
+ errorMessage: error.error.message,
126
+ }, error);
127
+ }
128
+ else {
129
+ (0, standardErrors_1.throwError)(error);
130
+ }
131
+ }
132
+ }
133
+ exports.downloadGithubRepoContents = downloadGithubRepoContents;
@@ -0,0 +1 @@
1
+ export declare function checkAndAddConfigToGitignore(configPath: string): void;