@hubspot/project-parsing-lib 0.2.0 → 0.2.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 (66) hide show
  1. package/README.md +4 -35
  2. package/package.json +124 -22
  3. package/src/exports/constants.d.ts +1 -0
  4. package/src/exports/constants.js +1 -0
  5. package/src/exports/lang.d.ts +1 -0
  6. package/src/exports/lang.js +1 -0
  7. package/src/exports/migrate.d.ts +1 -0
  8. package/src/exports/migrate.js +1 -0
  9. package/src/exports/profiles.d.ts +3 -0
  10. package/src/exports/profiles.js +2 -0
  11. package/src/exports/projects.d.ts +4 -0
  12. package/src/exports/projects.js +3 -0
  13. package/src/exports/schema.d.ts +2 -0
  14. package/src/exports/schema.js +1 -0
  15. package/src/exports/themes.d.ts +2 -0
  16. package/src/exports/themes.js +1 -0
  17. package/src/exports/transform.d.ts +2 -0
  18. package/src/exports/transform.js +1 -0
  19. package/src/exports/translate.d.ts +3 -0
  20. package/src/exports/translate.js +2 -0
  21. package/src/exports/uid.d.ts +1 -0
  22. package/src/exports/uid.js +1 -0
  23. package/src/exports/validation.d.ts +3 -0
  24. package/src/exports/validation.js +2 -0
  25. package/src/exports/workspaces.d.ts +2 -0
  26. package/src/exports/workspaces.js +1 -0
  27. package/src/lang/copy.d.ts +8 -1
  28. package/src/lang/copy.js +30 -33
  29. package/src/lib/constants.d.ts +55 -28
  30. package/src/lib/constants.js +172 -121
  31. package/src/lib/errors.d.ts +4 -3
  32. package/src/lib/errors.js +62 -38
  33. package/src/lib/files.d.ts +10 -1
  34. package/src/lib/files.js +51 -40
  35. package/src/lib/localDev.d.ts +4 -0
  36. package/src/lib/localDev.js +72 -0
  37. package/src/lib/migrate.d.ts +1 -0
  38. package/src/lib/migrate.js +43 -45
  39. package/src/lib/migrateThemes.d.ts +25 -0
  40. package/src/lib/migrateThemes.js +120 -0
  41. package/src/lib/minimalArboristTree.d.ts +118 -0
  42. package/src/lib/minimalArboristTree.js +32 -0
  43. package/src/lib/platformVersion.d.ts +3 -0
  44. package/src/lib/platformVersion.js +16 -0
  45. package/src/lib/profiles.d.ts +6 -1
  46. package/src/lib/profiles.js +95 -40
  47. package/src/lib/project.d.ts +13 -0
  48. package/src/lib/project.js +36 -0
  49. package/src/lib/schemas.d.ts +2 -2
  50. package/src/lib/schemas.js +11 -11
  51. package/src/lib/transform.d.ts +4 -2
  52. package/src/lib/transform.js +100 -53
  53. package/src/lib/translate.d.ts +3 -0
  54. package/src/lib/translate.js +62 -0
  55. package/src/lib/types.d.ts +30 -6
  56. package/src/lib/types.js +1 -2
  57. package/src/lib/uid.d.ts +2 -0
  58. package/src/lib/uid.js +14 -9
  59. package/src/lib/utils.d.ts +3 -0
  60. package/src/lib/utils.js +16 -0
  61. package/src/lib/validation.d.ts +4 -4
  62. package/src/lib/validation.js +61 -53
  63. package/src/lib/workspaces.d.ts +68 -0
  64. package/src/lib/workspaces.js +290 -0
  65. package/src/index.d.ts +0 -18
  66. package/src/index.js +0 -86
@@ -1,6 +1,6 @@
1
- import { ErrorObject } from 'ajv/dist/2020';
1
+ import type { ErrorObject } from 'ajv';
2
2
  export type Dependencies = Record<string, string | string[]>;
3
- export interface Components {
3
+ export interface ComponentMeta {
4
4
  uid: string;
5
5
  type: string;
6
6
  config: unknown;
@@ -15,7 +15,7 @@ export interface FileLoadResult extends FileActionResult {
15
15
  content?: string;
16
16
  }
17
17
  export interface FileParseResult extends FileActionResult {
18
- content?: Components;
18
+ content?: ComponentMeta;
19
19
  }
20
20
  export interface IntermediateRepresentationNode {
21
21
  componentType: string;
@@ -25,21 +25,37 @@ export interface IntermediateRepresentationNode {
25
25
  config: unknown;
26
26
  files: unknown;
27
27
  }
28
+ export type BEProfileVariableType = 'PROFILE_INT' | 'PROFILE_LONG' | 'PROFILE_STRING' | 'PROFILE_BOOLEAN';
29
+ export interface BEProfileVariables {
30
+ [key: string]: {
31
+ variableType: BEProfileVariableType;
32
+ value: HSProfileVariableType;
33
+ };
34
+ }
28
35
  export interface IntermediateRepresentation {
29
36
  intermediateNodesIndexedByUid: {
30
37
  [key: string]: IntermediateRepresentationNode;
31
38
  };
39
+ profileData?: {
40
+ vars: {
41
+ profileVariables: BEProfileVariables;
42
+ };
43
+ };
32
44
  }
33
45
  export interface IntermediateRepresentationNodeLocalDev extends IntermediateRepresentationNode {
34
46
  localDev: {
35
47
  componentRoot: string;
36
48
  componentConfigPath: string;
49
+ configUpdatedSinceLastUpload: boolean;
50
+ removed: boolean;
51
+ parsingErrors: string[];
37
52
  };
38
53
  }
39
54
  export interface IntermediateRepresentationLocalDev {
40
55
  intermediateNodesIndexedByUid: {
41
56
  [key: string]: IntermediateRepresentationNodeLocalDev;
42
57
  };
58
+ profileData: HSProfileVariables;
43
59
  }
44
60
  export type Transformation = {
45
61
  intermediateRepresentation?: IntermediateRepresentationNode | null;
@@ -58,9 +74,17 @@ export interface TranslationOptions {
58
74
  skipValidation?: boolean;
59
75
  profile?: string;
60
76
  }
77
+ export interface TranslationOptionsLocalDev extends TranslationOptions {
78
+ projectNodesAtLastUpload?: {
79
+ [key: string]: IntermediateRepresentationNodeLocalDev;
80
+ };
81
+ }
82
+ type HSProfileVariableType = string | number | boolean;
83
+ export type HSProfileVariables = {
84
+ [key: string]: HSProfileVariableType;
85
+ };
61
86
  export interface HsProfileFile {
62
87
  accountId: number;
63
- variables?: {
64
- [key: string]: string | number | boolean;
65
- };
88
+ variables?: HSProfileVariables;
66
89
  }
90
+ export {};
package/src/lib/types.js CHANGED
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
package/src/lib/uid.d.ts CHANGED
@@ -1 +1,3 @@
1
+ export declare const MAX_UID_LENGTH = 64;
1
2
  export declare function validateUid(uid: string): string | undefined;
3
+ export declare function coerceToValidUid(potentialUid: string): string | undefined;
package/src/lib/uid.js CHANGED
@@ -1,15 +1,20 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateUid = validateUid;
4
- const copy_1 = require("../lang/copy");
5
- function validateUid(uid) {
1
+ import { errorMessages } from '../lang/copy.js';
2
+ export const MAX_UID_LENGTH = 64;
3
+ export function validateUid(uid) {
6
4
  if (uid === '' || !uid) {
7
- return copy_1.errorMessages.validation.emptyUid;
5
+ return errorMessages.validation.emptyUid;
8
6
  }
9
- if (uid.length > 64) {
10
- return copy_1.errorMessages.validation.uidTooLong;
7
+ if (uid.length > MAX_UID_LENGTH) {
8
+ return errorMessages.validation.uidTooLong;
11
9
  }
12
10
  if (!/^[a-zA-Z0-9_\-.]+$/.test(uid)) {
13
- return copy_1.errorMessages.validation.invalidUid;
11
+ return errorMessages.validation.invalidUid;
14
12
  }
15
13
  }
14
+ export function coerceToValidUid(potentialUid) {
15
+ if (typeof potentialUid !== 'string') {
16
+ return undefined;
17
+ }
18
+ const newUid = potentialUid.replace(/[^a-zA-Z0-9_\-.]/g, '');
19
+ return newUid.length === 0 ? undefined : newUid.substring(0, MAX_UID_LENGTH);
20
+ }
@@ -0,0 +1,3 @@
1
+ import { BEProfileVariableType } from './types.js';
2
+ export declare function loadJsonFile(filename: string): any;
3
+ export declare function getJavaNumberType(value: number): BEProfileVariableType;
@@ -0,0 +1,16 @@
1
+ import fs from 'fs';
2
+ import { PROFILE_VARIABLE_TYPES } from './constants.js';
3
+ export function loadJsonFile(filename) {
4
+ return JSON.parse(fs.readFileSync(filename, {
5
+ encoding: 'utf-8',
6
+ }));
7
+ }
8
+ export function getJavaNumberType(value) {
9
+ const JAVA_INT_MIN = -2_147_483_648;
10
+ const JAVA_INT_MAX = 2_147_483_647;
11
+ // Check if the value fits in Java int range
12
+ if (value >= JAVA_INT_MIN && value <= JAVA_INT_MAX) {
13
+ return PROFILE_VARIABLE_TYPES.PROFILE_INT;
14
+ }
15
+ return PROFILE_VARIABLE_TYPES.PROFILE_LONG;
16
+ }
@@ -1,5 +1,5 @@
1
- import { CompiledError, IntermediateRepresentation, Transformation, TranslationContext } from './types';
2
- import Ajv, { ValidateFunction } from 'ajv/dist/2020';
1
+ import { CompiledError, IntermediateRepresentation, Transformation, TranslationContext } from './types.js';
2
+ import { Ajv2020, ValidateFunction } from 'ajv/dist/2020.js';
3
3
  export type ValidResult = {
4
4
  valid: true;
5
5
  errors?: null;
@@ -10,5 +10,5 @@ export type ValidationResults = {
10
10
  errors?: CompiledError;
11
11
  schemaValidationErrors?: ValidateFunction['errors'];
12
12
  } | ValidResult;
13
- export declare function createAjvInstance(): Ajv;
14
- export declare function validateIntermediateRepresentation(intermediateRepresentation: IntermediateRepresentation, transformation: Transformation[], translationContext: TranslationContext): Promise<ValidResult>;
13
+ export declare function createAjvInstance(): Ajv2020;
14
+ export declare function validateIntermediateRepresentation(intermediateRepresentation: IntermediateRepresentation, transformations: Transformation[], translationContext: TranslationContext): Promise<ValidResult>;
@@ -1,85 +1,84 @@
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.createAjvInstance = createAjvInstance;
7
- exports.validateIntermediateRepresentation = validateIntermediateRepresentation;
8
- const schemas_1 = require("./schemas");
9
- const _2020_1 = __importDefault(require("ajv/dist/2020"));
10
- const errors_1 = require("./errors");
11
- const copy_1 = require("../lang/copy");
12
- const constants_1 = require("./constants");
13
- const path_1 = __importDefault(require("path"));
14
- const transform_1 = require("./transform");
15
- const ajv_formats_1 = __importDefault(require("ajv-formats"));
16
- const uid_1 = require("./uid");
17
- function createAjvInstance() {
18
- const ajv = new _2020_1.default({ allErrors: true });
19
- (0, ajv_formats_1.default)(ajv);
1
+ import { getIntermediateRepresentationSchema } from './schemas.js';
2
+ import { Ajv2020, } from 'ajv/dist/2020.js';
3
+ import { compileError, TranslationError } from './errors.js';
4
+ import { errorMessages } from '../lang/copy.js';
5
+ import { AUTO_GENERATED_COMPONENT_TYPES, Components } from './constants.js';
6
+ import path from 'path';
7
+ import { findTransformationByUid, mapToUserFacingType } from './transform.js';
8
+ import { validateUid } from './uid.js';
9
+ // ajv-formats does not play nicely with ESM and TS
10
+ import addFormatsModule from 'ajv-formats';
11
+ const addFormats = addFormatsModule;
12
+ export function createAjvInstance() {
13
+ const ajv = new Ajv2020({
14
+ allErrors: true,
15
+ code: { esm: true },
16
+ strict: false, // Allow Draft 2020-12 features
17
+ });
18
+ addFormats(ajv);
20
19
  return ajv;
21
20
  }
22
- function validateIntermediateRepresentationNode(schema, transformation, irNode, index, translationContext) {
23
- if (constants_1.AutoGeneratedComponentTypes.includes((0, transform_1.mapToUserFacingType)(irNode.componentType))) {
21
+ function validateIntermediateRepresentationNode(schema, transformation, irNode, translationContext) {
22
+ if (AUTO_GENERATED_COMPONENT_TYPES.includes(mapToUserFacingType(irNode.componentType))) {
24
23
  // Skip validation for auto-generated components
25
24
  return {
26
25
  valid: true,
27
26
  };
28
27
  }
29
- if (transformation[index].fileParseResult.errors.length > 0) {
28
+ if (transformation.fileParseResult.errors.length > 0) {
30
29
  return {
31
30
  valid: false,
32
- errors: (0, errors_1.compileError)(transformation[index]),
31
+ errors: compileError(transformation),
33
32
  };
34
33
  }
35
34
  const ajv = createAjvInstance();
36
35
  let shouldSkipValidation = false;
37
36
  if (!irNode.uid) {
38
- transformation[index].fileParseResult.errors.push(copy_1.errorMessages.validation.missingUid);
37
+ transformation.fileParseResult.errors.push(errorMessages.validation.missingUid);
39
38
  }
40
39
  else {
41
- const uidValidationResult = (0, uid_1.validateUid)(irNode.uid);
40
+ const uidValidationResult = validateUid(irNode.uid);
42
41
  if (uidValidationResult) {
43
- transformation[index].fileParseResult.errors.push(uidValidationResult);
42
+ transformation.fileParseResult.errors.push(uidValidationResult);
44
43
  }
45
44
  }
46
45
  if (!irNode.config) {
47
- transformation[index].fileParseResult.errors.push(copy_1.errorMessages.validation.missingConfig);
46
+ transformation.fileParseResult.errors.push(errorMessages.validation.missingConfig);
48
47
  // If there is no config block, there is nothing to validation
49
48
  shouldSkipValidation = true;
50
49
  }
51
50
  if (!schema[irNode.componentType]) {
52
51
  if (!irNode.componentType) {
53
- transformation[index].fileParseResult.errors.push(copy_1.errorMessages.validation.missingType);
52
+ transformation.fileParseResult.errors.push(errorMessages.validation.missingType);
54
53
  }
55
54
  else {
56
- transformation[index].fileParseResult.errors.push(copy_1.errorMessages.validation.unsupportedType(irNode.componentType));
55
+ transformation.fileParseResult.errors.push(errorMessages.validation.unsupportedType(irNode.componentType));
57
56
  }
58
57
  // If there is no schema for the component type, there is no way to validate
59
58
  shouldSkipValidation = true;
60
59
  }
61
- const userFacingType = (0, transform_1.mapToUserFacingType)(irNode.componentType);
62
- const component = constants_1.Components[userFacingType];
60
+ const userFacingType = mapToUserFacingType(irNode.componentType);
61
+ const component = Components[userFacingType];
63
62
  if (userFacingType && component) {
64
63
  const expectedParentDir = component.parentComponent
65
- ? constants_1.Components[component.parentComponent].dir
64
+ ? Components[component.parentComponent].dir
66
65
  : '';
67
- const expectedLocation = path_1.default.join(expectedParentDir, component.dir);
68
- const actualLocation = path_1.default.dirname(transformation[index].fileParseResult.file);
66
+ const expectedLocation = path.join(expectedParentDir, component.dir);
67
+ const actualLocation = path.dirname(transformation.fileParseResult.file);
69
68
  if (expectedLocation !== actualLocation) {
70
- transformation[index].fileParseResult.errors.push(copy_1.errorMessages.validation.wrongDirectoryForComponent(actualLocation, userFacingType, component, path_1.default.join(translationContext.projectSourceDir, expectedLocation)));
69
+ transformation.fileParseResult.errors.push(errorMessages.validation.wrongDirectoryForComponent(actualLocation, userFacingType, component, path.join(translationContext.projectSourceDir, expectedLocation)));
71
70
  }
72
71
  }
73
72
  if (shouldSkipValidation) {
74
73
  return {
75
74
  valid: false,
76
- errors: (0, errors_1.compileError)(transformation[index]),
75
+ errors: compileError(transformation),
77
76
  };
78
77
  }
79
78
  const validate = ajv.compile(schema[irNode.componentType]);
80
79
  const valid = validate(irNode.config);
81
80
  if (valid) {
82
- const { errors } = transformation[index].fileParseResult;
81
+ const { errors } = transformation.fileParseResult;
83
82
  // Even through it passed the schema validation, it may have had other errors along the way
84
83
  return errors.length === 0
85
84
  ? {
@@ -87,7 +86,7 @@ function validateIntermediateRepresentationNode(schema, transformation, irNode,
87
86
  }
88
87
  : {
89
88
  valid: false,
90
- errors: (0, errors_1.compileError)(transformation[index]),
89
+ errors: compileError(transformation),
91
90
  };
92
91
  }
93
92
  return {
@@ -95,18 +94,24 @@ function validateIntermediateRepresentationNode(schema, transformation, irNode,
95
94
  schemaValidationErrors: validate.errors,
96
95
  };
97
96
  }
98
- async function validateIntermediateRepresentation(intermediateRepresentation, transformation, translationContext) {
99
- const schema = await (0, schemas_1.getIntermediateRepresentationSchema)(translationContext);
97
+ export async function validateIntermediateRepresentation(intermediateRepresentation, transformations, translationContext) {
98
+ const hasAnyFileParseErrors = transformations.some(t => t.fileParseResult.errors.length > 0);
99
+ const schema = await getIntermediateRepresentationSchema(translationContext);
100
100
  const potentialDuplicatesByComponent = {};
101
- const results = Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).map((irNode, index) => {
102
- const userFacingType = (0, transform_1.mapToUserFacingType)(irNode.componentType);
103
- const component = constants_1.Components[userFacingType];
101
+ const results = Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).map(irNode => {
102
+ const userFacingType = mapToUserFacingType(irNode.componentType);
103
+ const component = Components[userFacingType];
104
104
  if (component && component.singularComponent) {
105
- potentialDuplicatesByComponent[userFacingType] = potentialDuplicatesByComponent[userFacingType]
106
- ? [...potentialDuplicatesByComponent[userFacingType], irNode]
107
- : [irNode];
105
+ potentialDuplicatesByComponent[userFacingType] =
106
+ potentialDuplicatesByComponent[userFacingType]
107
+ ? [...potentialDuplicatesByComponent[userFacingType], irNode]
108
+ : [irNode];
109
+ }
110
+ const transformation = findTransformationByUid(transformations, irNode.uid);
111
+ if (!transformation) {
112
+ return { valid: false, errors: [errorMessages.validation.missingUid] };
108
113
  }
109
- return validateIntermediateRepresentationNode(schema, transformation, irNode, index, translationContext);
114
+ return validateIntermediateRepresentationNode(schema, transformation, irNode, translationContext);
110
115
  });
111
116
  let hasDuplicates = false;
112
117
  Object.entries(potentialDuplicatesByComponent).forEach(([componentType, potentialDuplicates]) => {
@@ -115,17 +120,20 @@ async function validateIntermediateRepresentation(intermediateRepresentation, tr
115
120
  }
116
121
  hasDuplicates = true;
117
122
  potentialDuplicates.forEach(duplicate => {
118
- transformation
119
- .find(transform => transform.intermediateRepresentation?.uid === duplicate.uid)
120
- ?.fileParseResult.errors.push(copy_1.errorMessages.project.duplicateComponent(componentType));
123
+ const potentialDuplicateTransformation = findTransformationByUid(transformations, duplicate.uid);
124
+ if (potentialDuplicateTransformation) {
125
+ potentialDuplicateTransformation.fileParseResult.errors.push(errorMessages.project.duplicateComponent(componentType));
126
+ }
121
127
  });
122
128
  });
123
- const valid = !hasDuplicates && results.every(result => result.valid);
129
+ const valid = !hasAnyFileParseErrors &&
130
+ !hasDuplicates &&
131
+ results.every(result => result.valid);
124
132
  if (valid) {
125
133
  return {
126
134
  valid,
127
135
  };
128
136
  }
129
- const schemaValidationErrors = results.map(result => result.schemaValidationErrors);
130
- throw new errors_1.TranslationError(copy_1.errorMessages.project.failedToTranslateProject, transformation, schemaValidationErrors, translationContext);
137
+ const schemaValidationErrors = results.map(result => 'schemaValidationErrors' in result ? result.schemaValidationErrors : null);
138
+ throw new TranslationError(errorMessages.project.failedToTranslateProject, transformations, schemaValidationErrors, translationContext);
131
139
  }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Error thrown when a workspace directory cannot be resolved
3
+ */
4
+ export declare class WorkspaceResolutionError extends Error {
5
+ workspaceDir: string;
6
+ sourcePackageJson: string;
7
+ constructor(workspaceDir: string, sourcePackageJson: string, cause?: Error);
8
+ }
9
+ /**
10
+ * Error thrown when a file: dependency cannot be resolved
11
+ */
12
+ export declare class FileDependencyResolutionError extends Error {
13
+ packageName: string;
14
+ dependencyPath: string;
15
+ sourcePackageJson: string;
16
+ constructor(packageName: string, dependencyPath: string, sourcePackageJson: string, cause?: Error);
17
+ }
18
+ interface PackageJson {
19
+ name?: string;
20
+ workspaces?: string[] | {
21
+ packages?: string[];
22
+ nohoist?: string[];
23
+ };
24
+ dependencies?: Record<string, string>;
25
+ }
26
+ export interface ParsedPackageJson {
27
+ path: string;
28
+ dir: string;
29
+ content: PackageJson | null;
30
+ }
31
+ export interface WorkspaceMapping {
32
+ workspaceDir: string;
33
+ sourcePackageJsonPath: string;
34
+ }
35
+ export interface FileDependencyMapping {
36
+ packageName: string;
37
+ localPath: string;
38
+ sourcePackageJsonPath: string;
39
+ }
40
+ /**
41
+ * Finds and parses all package.json files in a directory.
42
+ * This is the single entry point for discovering package.json files and parsing their contents.
43
+ */
44
+ export declare function findAndParsePackageJsonFiles(srcDir: string): Promise<ParsedPackageJson[]>;
45
+ /**
46
+ * Resolves workspace glob patterns to actual directories
47
+ */
48
+ export declare function resolveWorkspaceDirectories(baseDir: string, workspaceGlobs: string[]): Promise<string[]>;
49
+ /**
50
+ * Collects all workspace directories that need to be uploaded.
51
+ * Handles edge cases like circular references, symlinks, and overlapping paths.
52
+ * Returns mappings that track which package.json defined each workspace.
53
+ */
54
+ export declare function collectWorkspaceDirectories(parsedPackageJsons: ParsedPackageJson[]): Promise<WorkspaceMapping[]>;
55
+ /**
56
+ * Collects all file: dependencies that need to be uploaded.
57
+ * Returns mappings that track the package name, resolved path, and source package.json.
58
+ */
59
+ export declare function collectFileDependencies(parsedPackageJsons: ParsedPackageJson[]): Promise<FileDependencyMapping[]>;
60
+ /**
61
+ * Returns the set of files that npm would include when publishing a package.
62
+ * Uses npm-packlist which respects the "files" field in package.json,
63
+ * .npmignore, and .gitignore rules.
64
+ *
65
+ * @throws Error if packlist fails to generate the file list
66
+ */
67
+ export declare function getPackableFiles(dir: string): Promise<Set<string>>;
68
+ export {};