@hubspot/project-parsing-lib 0.10.0-beta.0 → 0.10.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.
- package/package.json +6 -4
- package/src/index.js +3 -3
- package/src/lib/transform.d.ts +1 -0
- package/src/lib/transform.js +4 -0
- package/src/lib/validation.d.ts +1 -1
- package/src/lib/validation.js +29 -22
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/project-parsing-lib",
|
|
3
|
-
"version": "0.10.0
|
|
3
|
+
"version": "0.10.0",
|
|
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",
|
|
7
7
|
"devDependencies": {
|
|
8
8
|
"@hubspot/local-dev-lib": "^3.18.0",
|
|
9
|
+
"@hubspot/npm-scripts": "0.0.4",
|
|
9
10
|
"@inquirer/prompts": "^7.1.0",
|
|
10
11
|
"@types/jest": "^29.5.14",
|
|
11
12
|
"@types/semver": "^7.5.8",
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
"prettier": "^1.19.1",
|
|
20
21
|
"semver": "^7.6.3",
|
|
21
22
|
"ts-jest": "^29.2.5",
|
|
22
|
-
"
|
|
23
|
+
"tsx": "^4.20.5",
|
|
23
24
|
"typescript": "^5.6.2"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
@@ -30,13 +31,14 @@
|
|
|
30
31
|
"@hubspot/local-dev-lib": "^3.16.0"
|
|
31
32
|
},
|
|
32
33
|
"scripts": {
|
|
33
|
-
"build": "
|
|
34
|
+
"build": "tsx ./scripts/build.ts",
|
|
34
35
|
"lint": "echo 'Linting is disabled for Blazar'",
|
|
35
36
|
"lint:local": "eslint --max-warnings=0 . && prettier ./src/** --check",
|
|
36
37
|
"local-dev": "yarn build && cd dist && yarn link && cd .. && tsc --watch --rootDir . --outdir dist",
|
|
38
|
+
"local-link": "hubspot-linking",
|
|
37
39
|
"prettier:write": "prettier ./src/** --write",
|
|
38
40
|
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ./node_modules/.bin/jest",
|
|
39
|
-
"release": "
|
|
41
|
+
"release": "tsx ./scripts/release.ts release",
|
|
40
42
|
"reset-north-star": "git submodule update --init",
|
|
41
43
|
"acceptance-test": "yarn --cwd=./acceptance-tests test",
|
|
42
44
|
"update-north-star": "git submodule update --remote"
|
package/src/index.js
CHANGED
|
@@ -21,11 +21,11 @@ async function translate(translationContext, translationOptions = defaultOptions
|
|
|
21
21
|
if (translationOptions.profile) {
|
|
22
22
|
hsProfileContents = (0, files_1.loadHsProfileFile)(translationContext.projectSourceDir, translationOptions.profile);
|
|
23
23
|
}
|
|
24
|
-
const
|
|
25
|
-
const intermediateRepresentation = (0, transform_1.getIntermediateRepresentation)(
|
|
24
|
+
const transformations = (0, transform_1.transform)(metafileContents, translationContext, hsProfileContents);
|
|
25
|
+
const intermediateRepresentation = (0, transform_1.getIntermediateRepresentation)(transformations, hsProfileContents, skipValidation);
|
|
26
26
|
// Remove once extensions and serverless functions are supported
|
|
27
27
|
if (!skipValidation) {
|
|
28
|
-
await (0, validation_1.validateIntermediateRepresentation)(intermediateRepresentation,
|
|
28
|
+
await (0, validation_1.validateIntermediateRepresentation)(intermediateRepresentation, transformations, translationContext);
|
|
29
29
|
}
|
|
30
30
|
return intermediateRepresentation;
|
|
31
31
|
}
|
package/src/lib/transform.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Dependencies, FileParseResult, HsProfileFile, IntermediateRepresentatio
|
|
|
2
2
|
export declare function mapToInternalType(type: string): string;
|
|
3
3
|
export declare function mapToUserFacingType(type: string): string;
|
|
4
4
|
export declare function mapToUserFriendlyName(internalType: string): string;
|
|
5
|
+
export declare function findTransformationByUid(transformations: Transformation[], uid: string): Transformation | undefined;
|
|
5
6
|
export declare function transform(fileParseResults: FileParseResult[], translationContext: TranslationContext, hsProfileContents?: HsProfileFile): Transformation[];
|
|
6
7
|
export declare function getIntermediateRepresentation(transformations: Transformation[], hsProfileContents?: HsProfileFile, skipValidation?: boolean): IntermediateRepresentation;
|
|
7
8
|
export declare function getParsingErrors(transformations: Transformation[]): FileParseResult[];
|
package/src/lib/transform.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.mapToInternalType = mapToInternalType;
|
|
7
7
|
exports.mapToUserFacingType = mapToUserFacingType;
|
|
8
8
|
exports.mapToUserFriendlyName = mapToUserFriendlyName;
|
|
9
|
+
exports.findTransformationByUid = findTransformationByUid;
|
|
9
10
|
exports.transform = transform;
|
|
10
11
|
exports.getIntermediateRepresentation = getIntermediateRepresentation;
|
|
11
12
|
exports.getParsingErrors = getParsingErrors;
|
|
@@ -59,6 +60,9 @@ function mapToUserFacingType(type) {
|
|
|
59
60
|
function mapToUserFriendlyName(internalType) {
|
|
60
61
|
return constants_1.Components[mapToUserFacingType(internalType)]?.userFriendlyName || '';
|
|
61
62
|
}
|
|
63
|
+
function findTransformationByUid(transformations, uid) {
|
|
64
|
+
return transformations.find(t => t.intermediateRepresentation?.uid === uid);
|
|
65
|
+
}
|
|
62
66
|
function transform(fileParseResults, translationContext, hsProfileContents) {
|
|
63
67
|
const parentTypes = Object.keys(constants_1.ProjectStructure);
|
|
64
68
|
const parentComponents = {};
|
package/src/lib/validation.d.ts
CHANGED
|
@@ -11,4 +11,4 @@ export type ValidationResults = {
|
|
|
11
11
|
schemaValidationErrors?: ValidateFunction['errors'];
|
|
12
12
|
} | ValidResult;
|
|
13
13
|
export declare function createAjvInstance(): Ajv;
|
|
14
|
-
export declare function validateIntermediateRepresentation(intermediateRepresentation: IntermediateRepresentation,
|
|
14
|
+
export declare function validateIntermediateRepresentation(intermediateRepresentation: IntermediateRepresentation, transformations: Transformation[], translationContext: TranslationContext): Promise<ValidResult>;
|
package/src/lib/validation.js
CHANGED
|
@@ -19,41 +19,41 @@ function createAjvInstance() {
|
|
|
19
19
|
(0, ajv_formats_1.default)(ajv);
|
|
20
20
|
return ajv;
|
|
21
21
|
}
|
|
22
|
-
function validateIntermediateRepresentationNode(schema, transformation, irNode,
|
|
22
|
+
function validateIntermediateRepresentationNode(schema, transformation, irNode, translationContext) {
|
|
23
23
|
if (constants_1.AutoGeneratedComponentTypes.includes((0, transform_1.mapToUserFacingType)(irNode.componentType))) {
|
|
24
24
|
// Skip validation for auto-generated components
|
|
25
25
|
return {
|
|
26
26
|
valid: true,
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
if (transformation
|
|
29
|
+
if (transformation.fileParseResult.errors.length > 0) {
|
|
30
30
|
return {
|
|
31
31
|
valid: false,
|
|
32
|
-
errors: (0, errors_1.compileError)(transformation
|
|
32
|
+
errors: (0, errors_1.compileError)(transformation),
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
const ajv = createAjvInstance();
|
|
36
36
|
let shouldSkipValidation = false;
|
|
37
37
|
if (!irNode.uid) {
|
|
38
|
-
transformation
|
|
38
|
+
transformation.fileParseResult.errors.push(copy_1.errorMessages.validation.missingUid);
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
41
|
const uidValidationResult = (0, uid_1.validateUid)(irNode.uid);
|
|
42
42
|
if (uidValidationResult) {
|
|
43
|
-
transformation
|
|
43
|
+
transformation.fileParseResult.errors.push(uidValidationResult);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
if (!irNode.config) {
|
|
47
|
-
transformation
|
|
47
|
+
transformation.fileParseResult.errors.push(copy_1.errorMessages.validation.missingConfig);
|
|
48
48
|
// If there is no config block, there is nothing to validation
|
|
49
49
|
shouldSkipValidation = true;
|
|
50
50
|
}
|
|
51
51
|
if (!schema[irNode.componentType]) {
|
|
52
52
|
if (!irNode.componentType) {
|
|
53
|
-
transformation
|
|
53
|
+
transformation.fileParseResult.errors.push(copy_1.errorMessages.validation.missingType);
|
|
54
54
|
}
|
|
55
55
|
else {
|
|
56
|
-
transformation
|
|
56
|
+
transformation.fileParseResult.errors.push(copy_1.errorMessages.validation.unsupportedType(irNode.componentType));
|
|
57
57
|
}
|
|
58
58
|
// If there is no schema for the component type, there is no way to validate
|
|
59
59
|
shouldSkipValidation = true;
|
|
@@ -65,21 +65,23 @@ function validateIntermediateRepresentationNode(schema, transformation, irNode,
|
|
|
65
65
|
? constants_1.Components[component.parentComponent].dir
|
|
66
66
|
: '';
|
|
67
67
|
const expectedLocation = path_1.default.join(expectedParentDir, component.dir);
|
|
68
|
-
const actualLocation = path_1.default.dirname(transformation
|
|
68
|
+
const actualLocation = path_1.default.dirname(transformation.fileParseResult.file);
|
|
69
69
|
if (expectedLocation !== actualLocation) {
|
|
70
|
-
transformation
|
|
70
|
+
transformation.fileParseResult.errors.push(copy_1.errorMessages.validation.wrongDirectoryForComponent(actualLocation, userFacingType, component, path_1.default.join(translationContext.projectSourceDir, expectedLocation)));
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
if (shouldSkipValidation) {
|
|
74
74
|
return {
|
|
75
75
|
valid: false,
|
|
76
|
-
errors: (0, errors_1.compileError)(transformation
|
|
76
|
+
errors: (0, errors_1.compileError)(transformation),
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
|
+
console.log('schema[irNode.componentType]', schema[irNode.componentType]);
|
|
80
|
+
console.log('irNode.config', irNode.config);
|
|
79
81
|
const validate = ajv.compile(schema[irNode.componentType]);
|
|
80
82
|
const valid = validate(irNode.config);
|
|
81
83
|
if (valid) {
|
|
82
|
-
const { errors } = transformation
|
|
84
|
+
const { errors } = transformation.fileParseResult;
|
|
83
85
|
// Even through it passed the schema validation, it may have had other errors along the way
|
|
84
86
|
return errors.length === 0
|
|
85
87
|
? {
|
|
@@ -87,7 +89,7 @@ function validateIntermediateRepresentationNode(schema, transformation, irNode,
|
|
|
87
89
|
}
|
|
88
90
|
: {
|
|
89
91
|
valid: false,
|
|
90
|
-
errors: (0, errors_1.compileError)(transformation
|
|
92
|
+
errors: (0, errors_1.compileError)(transformation),
|
|
91
93
|
};
|
|
92
94
|
}
|
|
93
95
|
return {
|
|
@@ -95,11 +97,11 @@ function validateIntermediateRepresentationNode(schema, transformation, irNode,
|
|
|
95
97
|
schemaValidationErrors: validate.errors,
|
|
96
98
|
};
|
|
97
99
|
}
|
|
98
|
-
async function validateIntermediateRepresentation(intermediateRepresentation,
|
|
99
|
-
const hasAnyFileParseErrors =
|
|
100
|
+
async function validateIntermediateRepresentation(intermediateRepresentation, transformations, translationContext) {
|
|
101
|
+
const hasAnyFileParseErrors = transformations.some(t => t.fileParseResult.errors.length > 0);
|
|
100
102
|
const schema = await (0, schemas_1.getIntermediateRepresentationSchema)(translationContext);
|
|
101
103
|
const potentialDuplicatesByComponent = {};
|
|
102
|
-
const results = Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).map(
|
|
104
|
+
const results = Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).map(irNode => {
|
|
103
105
|
const userFacingType = (0, transform_1.mapToUserFacingType)(irNode.componentType);
|
|
104
106
|
const component = constants_1.Components[userFacingType];
|
|
105
107
|
if (component && component.singularComponent) {
|
|
@@ -107,7 +109,11 @@ async function validateIntermediateRepresentation(intermediateRepresentation, tr
|
|
|
107
109
|
? [...potentialDuplicatesByComponent[userFacingType], irNode]
|
|
108
110
|
: [irNode];
|
|
109
111
|
}
|
|
110
|
-
|
|
112
|
+
const transformation = (0, transform_1.findTransformationByUid)(transformations, irNode.uid);
|
|
113
|
+
if (!transformation) {
|
|
114
|
+
return { valid: false, errors: [copy_1.errorMessages.validation.missingUid] };
|
|
115
|
+
}
|
|
116
|
+
return validateIntermediateRepresentationNode(schema, transformation, irNode, translationContext);
|
|
111
117
|
});
|
|
112
118
|
let hasDuplicates = false;
|
|
113
119
|
Object.entries(potentialDuplicatesByComponent).forEach(([componentType, potentialDuplicates]) => {
|
|
@@ -116,9 +122,10 @@ async function validateIntermediateRepresentation(intermediateRepresentation, tr
|
|
|
116
122
|
}
|
|
117
123
|
hasDuplicates = true;
|
|
118
124
|
potentialDuplicates.forEach(duplicate => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
const potentialDuplicateTransformation = (0, transform_1.findTransformationByUid)(transformations, duplicate.uid);
|
|
126
|
+
if (potentialDuplicateTransformation) {
|
|
127
|
+
potentialDuplicateTransformation.fileParseResult.errors.push(copy_1.errorMessages.project.duplicateComponent(componentType));
|
|
128
|
+
}
|
|
122
129
|
});
|
|
123
130
|
});
|
|
124
131
|
const valid = !hasAnyFileParseErrors &&
|
|
@@ -129,6 +136,6 @@ async function validateIntermediateRepresentation(intermediateRepresentation, tr
|
|
|
129
136
|
valid,
|
|
130
137
|
};
|
|
131
138
|
}
|
|
132
|
-
const schemaValidationErrors = results.map(result => result.schemaValidationErrors);
|
|
133
|
-
throw new errors_1.TranslationError(copy_1.errorMessages.project.failedToTranslateProject,
|
|
139
|
+
const schemaValidationErrors = results.map(result => 'schemaValidationErrors' in result ? result.schemaValidationErrors : null);
|
|
140
|
+
throw new errors_1.TranslationError(copy_1.errorMessages.project.failedToTranslateProject, transformations, schemaValidationErrors, translationContext);
|
|
134
141
|
}
|