@hubspot/project-parsing-lib 0.2.0 → 0.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/project-parsing-lib",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Parsing library for converting projects directory structures to their intermediate representation",
5
5
  "license": "Apache-2.0",
6
6
  "main": "src/index.js",
@@ -37,6 +37,7 @@
37
37
  "prettier:write": "prettier ./src/** --write",
38
38
  "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ./node_modules/.bin/jest",
39
39
  "release": "yarn ts-node ./scripts/release.ts release",
40
+ "reset-north-star": "git submodule update --init",
40
41
  "acceptance-test": "yarn --cwd=./acceptance-tests test",
41
42
  "update-north-star": "git submodule update --remote --merge"
42
43
  },
package/src/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export declare function translateForLocalDev(translationContext: TranslationCont
5
5
  export { isTranslationError } from './lib/errors';
6
6
  export { IntermediateRepresentation, IntermediateRepresentationNode, IntermediateRepresentationLocalDev, IntermediateRepresentationNodeLocalDev, TranslationContext, };
7
7
  export { getHsProfileFilename } from './lib/profiles';
8
- export { loadHsProfileFile, getAllHsProfiles } from './lib/files';
8
+ export { loadHsProfileFile, getAllHsProfiles, projectContainsHsMetaFiles, } from './lib/files';
9
9
  export { migrate } from './lib/migrate';
10
10
  export { validateUid } from './lib/uid';
11
11
  export { mapToUserFriendlyName, mapToInternalType } from './lib/transform';
package/src/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getUnsupportedTypeError = exports.getFailedToFetchSchemasError = exports.getMissingRequiredFieldError = exports.getMissingAccountIdError = exports.getMissingConfigError = exports.getMissingTypeError = exports.getInvalidJsonError = exports.AjvErrorKeyword = exports.hsProjectJsonFilename = exports.metafileExtension = exports.createAjvInstance = exports.getIntermediateRepresentationSchema = exports.mapToInternalType = exports.mapToUserFriendlyName = exports.validateUid = exports.migrate = exports.getAllHsProfiles = exports.loadHsProfileFile = exports.getHsProfileFilename = exports.isTranslationError = void 0;
6
+ exports.getUnsupportedTypeError = exports.getFailedToFetchSchemasError = exports.getMissingRequiredFieldError = exports.getMissingAccountIdError = exports.getMissingConfigError = exports.getMissingTypeError = exports.getInvalidJsonError = exports.AjvErrorKeyword = exports.hsProjectJsonFilename = exports.metafileExtension = exports.createAjvInstance = exports.getIntermediateRepresentationSchema = exports.mapToInternalType = exports.mapToUserFriendlyName = exports.validateUid = exports.migrate = exports.projectContainsHsMetaFiles = exports.getAllHsProfiles = exports.loadHsProfileFile = exports.getHsProfileFilename = exports.isTranslationError = void 0;
7
7
  exports.translate = translate;
8
8
  exports.translateForLocalDev = translateForLocalDev;
9
9
  const files_1 = require("./lib/files");
@@ -60,6 +60,7 @@ Object.defineProperty(exports, "getHsProfileFilename", { enumerable: true, get:
60
60
  var files_2 = require("./lib/files");
61
61
  Object.defineProperty(exports, "loadHsProfileFile", { enumerable: true, get: function () { return files_2.loadHsProfileFile; } });
62
62
  Object.defineProperty(exports, "getAllHsProfiles", { enumerable: true, get: function () { return files_2.getAllHsProfiles; } });
63
+ Object.defineProperty(exports, "projectContainsHsMetaFiles", { enumerable: true, get: function () { return files_2.projectContainsHsMetaFiles; } });
63
64
  var migrate_1 = require("./lib/migrate");
64
65
  Object.defineProperty(exports, "migrate", { enumerable: true, get: function () { return migrate_1.migrate; } });
65
66
  var uid_1 = require("./lib/uid");
package/src/lang/copy.js CHANGED
@@ -39,7 +39,7 @@ exports.errorMessages = {
39
39
  missingType: `Missing required field: 'type'`,
40
40
  missingConfig: `Missing required field: 'config'`,
41
41
  unsupportedType: (type) => `Unsupported type: ${(0, transform_1.mapToUserFacingType)(type)}`,
42
- errorWithField: (field, error) => `Error with ${field}: ${error || 'Unknown error'}`,
42
+ errorWithField: (field, error) => `${field}: ${error || 'Unknown error'}`,
43
43
  invalidJson: 'Invalid JSON',
44
44
  wrongDirectoryForComponent: (directory, componentType, componentMetadata, correctDir) => `The directory '${directory}' is incorrect for type '${componentType}'. ${componentMetadata.userFriendlyName} ${componentMetadata.userFriendlyTypePlural} should only be placed in the '${correctDir}' directory`,
45
45
  },
@@ -8,6 +8,7 @@ export declare class TranslationError extends Error {
8
8
  constructor(message: string, transformations: Transformation[], errors: (ErrorObject[] | null | undefined)[], translationContext: TranslationContext);
9
9
  toString(): string;
10
10
  }
11
+ export declare function extractDotNotationValueFromTransformation(dotNotation: string, transformation: Transformation): unknown;
11
12
  export declare const AjvErrorKeyword: {
12
13
  AdditionalItems: string;
13
14
  AdditionalProperties: string;
package/src/lib/errors.js CHANGED
@@ -6,8 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AjvErrorKeyword = exports.TranslationError = void 0;
7
7
  exports.isTranslationError = isTranslationError;
8
8
  exports.compileError = compileError;
9
+ exports.extractDotNotationValueFromTransformation = extractDotNotationValueFromTransformation;
9
10
  const copy_1 = require("../lang/copy");
10
11
  const path_1 = __importDefault(require("path"));
12
+ const logger_1 = require("@hubspot/local-dev-lib/logger");
11
13
  function isTranslationError(error) {
12
14
  return error instanceof TranslationError;
13
15
  }
@@ -33,9 +35,11 @@ class TranslationError extends Error {
33
35
  if (errors.length === 0) {
34
36
  return null;
35
37
  }
36
- return copy_1.errorMessages.validation.errorWithFileHeader(path_1.default.join(this.translationContext.projectSourceDir, file), errors);
38
+ const projectRoot = path_1.default.dirname(path_1.default.dirname(this.translationContext.projectSourceDir));
39
+ const relativePath = path_1.default.relative(projectRoot, path_1.default.join(this.translationContext.projectSourceDir, file));
40
+ return copy_1.errorMessages.validation.errorWithFileHeader(relativePath, errors);
37
41
  });
38
- return `${this.message}: ${listOfErrors
42
+ return `${this.message}:${listOfErrors
39
43
  .filter(error => error !== null)
40
44
  .join('')}`;
41
45
  }
@@ -61,16 +65,16 @@ function compileTranslationErrors(transformation, schemaErrors) {
61
65
  // If there is a one of error, we need to preprocess the errors to group them by instancePath and keyword
62
66
  // This allows us to group data from the errors that correspond to the same field
63
67
  if (hasOneOfErrors) {
64
- errors.push(...preprocessSpecialErrors(schemaErrors));
68
+ errors.push(...preprocessSpecialErrors(schemaErrors, transformation));
65
69
  }
66
70
  else {
67
71
  schemaErrors?.forEach(error => {
68
- errors.push(generateErrorMessage(error));
72
+ errors.push(generateErrorMessage(error, transformation));
69
73
  });
70
74
  }
71
75
  return { file, errors };
72
76
  }
73
- function preprocessSpecialErrors(schemaErrors) {
77
+ function preprocessSpecialErrors(schemaErrors, transformation) {
74
78
  const errors = [];
75
79
  const preprocessedErrors = {};
76
80
  schemaErrors?.forEach(error => {
@@ -94,11 +98,11 @@ function preprocessSpecialErrors(schemaErrors) {
94
98
  }
95
99
  return cur;
96
100
  });
97
- errors.push(generateErrorMessage(newValue));
101
+ errors.push(generateErrorMessage(newValue, transformation));
98
102
  });
99
103
  return errors;
100
104
  }
101
- function generateErrorMessage(error) {
105
+ function generateErrorMessage(error, transformation) {
102
106
  const errorPath = generateDotNotationPath(error);
103
107
  const errorMessage = copy_1.errorMessages.validation.errorWithField(errorPath, error.message);
104
108
  const params = Object.entries(error.params);
@@ -112,7 +116,11 @@ function generateErrorMessage(error) {
112
116
  return copy_1.errorMessages.validation.missingRequiredField(`${errorPath}.${error.params.missingProperty}`);
113
117
  }
114
118
  else if (isTypeError(error)) {
115
- return copy_1.errorMessages.validation.errorWithField(generateDotNotationPath(error), error.message);
119
+ const dotNotationPath = generateDotNotationPath(error);
120
+ const value = extractDotNotationValueFromTransformation(dotNotationPath, transformation);
121
+ return copy_1.errorMessages.validation.errorWithField(value !== undefined
122
+ ? `Value (${value}) in ${dotNotationPath}`
123
+ : dotNotationPath, error.message);
116
124
  }
117
125
  // Default case, it is not an error we know how to deal with
118
126
  return `${errorMessage} ${additionalContext.length > 0
@@ -134,23 +142,47 @@ function mergeEnumErrors(cur, next) {
134
142
  },
135
143
  };
136
144
  }
137
- const JSON_SCHEMA_VALIDATION_KEYWORDS = {
138
- required: 'required',
139
- type: 'type',
140
- oneOf: 'oneOf',
141
- enum: 'enum',
142
- };
145
+ function extractDotNotationValueFromTransformation(dotNotation, transformation) {
146
+ try {
147
+ const parts = dotNotation.split('.');
148
+ if (!transformation?.fileParseResult?.content || parts.length === 0) {
149
+ return undefined;
150
+ }
151
+ let value = transformation.fileParseResult.content;
152
+ // Traverse the nested objects and lists to get the end value
153
+ parts.forEach(item => {
154
+ // @ts-expect-error value is unknown since it is user generated config
155
+ const newValue = value[item];
156
+ if (newValue &&
157
+ typeof newValue === 'object' &&
158
+ !Array.isArray(newValue)) {
159
+ value = Object.assign(Object.create(null), newValue);
160
+ }
161
+ else {
162
+ value = newValue;
163
+ }
164
+ });
165
+ // If the value is an object/array, stringify the value so it doesn't log as [object Object] or '' for array
166
+ if (typeof value === 'object' && !!value) {
167
+ return JSON.stringify(value);
168
+ }
169
+ return value;
170
+ }
171
+ catch (e) {
172
+ logger_1.logger.debug('Unable to parse dot notation path to located value', e);
173
+ }
174
+ }
143
175
  function isOneOfError(error) {
144
- return error.keyword === JSON_SCHEMA_VALIDATION_KEYWORDS.oneOf;
176
+ return error.keyword === exports.AjvErrorKeyword.OneOf;
145
177
  }
146
178
  function isRequiredError(error) {
147
- return error.keyword === JSON_SCHEMA_VALIDATION_KEYWORDS.required;
179
+ return error.keyword === exports.AjvErrorKeyword.Required;
148
180
  }
149
181
  function isEnumError(error) {
150
- return error.keyword === JSON_SCHEMA_VALIDATION_KEYWORDS.enum;
182
+ return error.keyword === exports.AjvErrorKeyword.Enum;
151
183
  }
152
184
  function isTypeError(error) {
153
- return error.keyword === JSON_SCHEMA_VALIDATION_KEYWORDS.type;
185
+ return error.keyword === exports.AjvErrorKeyword.Type;
154
186
  }
155
187
  exports.AjvErrorKeyword = {
156
188
  AdditionalItems: 'additionalItems',
@@ -2,3 +2,4 @@ import { FileParseResult, TranslationContext, HsProfileFile } from './types';
2
2
  export declare function loadHsProfileFile(projectSourceDir: string, profile: string): HsProfileFile;
3
3
  export declare function getAllHsProfiles(projectSourceDir: string): Promise<string[]>;
4
4
  export declare function loadHsMetaFiles(translationContext: TranslationContext): Promise<FileParseResult[]>;
5
+ export declare function projectContainsHsMetaFiles(projectSourceDir: string): Promise<boolean>;
package/src/lib/files.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.loadHsProfileFile = loadHsProfileFile;
7
7
  exports.getAllHsProfiles = getAllHsProfiles;
8
8
  exports.loadHsMetaFiles = loadHsMetaFiles;
9
+ exports.projectContainsHsMetaFiles = projectContainsHsMetaFiles;
9
10
  const fs_1 = __importDefault(require("fs"));
10
11
  const path_1 = __importDefault(require("path"));
11
12
  const fs_2 = require("@hubspot/local-dev-lib/fs");
@@ -41,11 +42,10 @@ function getAllHsProfiles(projectSourceDir) {
41
42
  });
42
43
  }
43
44
  async function loadHsMetaFiles(translationContext) {
44
- const metaFiles = await locateHsMetaFiles(translationContext);
45
+ const metaFiles = await locateHsMetaFiles(translationContext.projectSourceDir);
45
46
  return parseHsMetaFiles(metaFiles, translationContext);
46
47
  }
47
- async function locateHsMetaFiles(translationContext) {
48
- const { projectSourceDir } = translationContext;
48
+ async function locateHsMetaFiles(projectSourceDir) {
49
49
  return (await (0, fs_2.walk)(projectSourceDir, ['node_modules'])).reduce((metaFiles, file) => {
50
50
  if (!file.endsWith(constants_1.metafileExtension)) {
51
51
  return metaFiles;
@@ -105,3 +105,7 @@ function parseFile(fileLoadResult) {
105
105
  }
106
106
  return { ...fileLoadResult, content: parsedFileContents };
107
107
  }
108
+ async function projectContainsHsMetaFiles(projectSourceDir) {
109
+ const hsMetaFiles = (await (0, fs_2.walk)(projectSourceDir, ['node_modules'])).filter(file => file.endsWith(constants_1.metafileExtension));
110
+ return hsMetaFiles.length > 0;
111
+ }