@hubspot/project-parsing-lib 0.6.1 → 0.8.0-beta.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 +2 -2
- package/src/index.d.ts +1 -0
- package/src/index.js +6 -3
- package/src/lib/constants.d.ts +1 -1
- package/src/lib/constants.js +5 -5
- package/src/lib/migrate.js +3 -7
- package/src/lib/migrateThemes.d.ts +25 -0
- package/src/lib/migrateThemes.js +113 -0
- package/src/lib/utils.d.ts +1 -1
- package/src/lib/utils.js +9 -26
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/project-parsing-lib",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-beta.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
|
-
"@hubspot/local-dev-lib": "^3.
|
|
8
|
+
"@hubspot/local-dev-lib": "^3.17.0",
|
|
9
9
|
"@inquirer/prompts": "^7.1.0",
|
|
10
10
|
"@types/jest": "^29.5.14",
|
|
11
11
|
"@types/semver": "^7.5.8",
|
package/src/index.d.ts
CHANGED
|
@@ -16,3 +16,4 @@ export { metafileExtension, hsProjectJsonFilename } from './lib/constants';
|
|
|
16
16
|
export { Components } from './lib/types';
|
|
17
17
|
export { AjvErrorKeyword } from './lib/errors';
|
|
18
18
|
export { getInvalidJsonError, getMissingTypeError, getMissingConfigError, getMissingAccountIdError, getMissingRequiredFieldError, getFailedToFetchSchemasError, getUnsupportedTypeError, } from './lang/copy';
|
|
19
|
+
export { migrateThemes, getProjectThemeDetails } from './lib/migrateThemes';
|
package/src/index.js
CHANGED
|
@@ -3,14 +3,14 @@ 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.projectContainsHsMetaFiles = exports.getAllHsProfiles = exports.loadHsProfileFile = exports.getHsProfileFilename = exports.isTranslationError = void 0;
|
|
6
|
+
exports.getProjectThemeDetails = exports.migrateThemes = 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
|
+
const isDeepEqual_1 = require("@hubspot/local-dev-lib/isDeepEqual");
|
|
9
10
|
const files_1 = require("./lib/files");
|
|
10
11
|
const validation_1 = require("./lib/validation");
|
|
11
12
|
const transform_1 = require("./lib/transform");
|
|
12
13
|
const copy_1 = require("./lang/copy");
|
|
13
|
-
const utils_1 = require("./lib/utils");
|
|
14
14
|
const path_1 = __importDefault(require("path"));
|
|
15
15
|
const defaultOptions = {
|
|
16
16
|
skipValidation: false,
|
|
@@ -49,7 +49,7 @@ async function translateForLocalDev(translationContext, translationOptions) {
|
|
|
49
49
|
if (translationOptions?.projectNodesAtLastUpload) {
|
|
50
50
|
const componentAtLastUpload = translationOptions.projectNodesAtLastUpload[uid];
|
|
51
51
|
if (componentAtLastUpload) {
|
|
52
|
-
configUpdatedSinceLastUpload = !(0,
|
|
52
|
+
configUpdatedSinceLastUpload = !(0, isDeepEqual_1.isDeepEqual)(component.config, componentAtLastUpload.config);
|
|
53
53
|
}
|
|
54
54
|
else {
|
|
55
55
|
// Component is net new
|
|
@@ -104,3 +104,6 @@ Object.defineProperty(exports, "getMissingAccountIdError", { enumerable: true, g
|
|
|
104
104
|
Object.defineProperty(exports, "getMissingRequiredFieldError", { enumerable: true, get: function () { return copy_2.getMissingRequiredFieldError; } });
|
|
105
105
|
Object.defineProperty(exports, "getFailedToFetchSchemasError", { enumerable: true, get: function () { return copy_2.getFailedToFetchSchemasError; } });
|
|
106
106
|
Object.defineProperty(exports, "getUnsupportedTypeError", { enumerable: true, get: function () { return copy_2.getUnsupportedTypeError; } });
|
|
107
|
+
var migrateThemes_1 = require("./lib/migrateThemes");
|
|
108
|
+
Object.defineProperty(exports, "migrateThemes", { enumerable: true, get: function () { return migrateThemes_1.migrateThemes; } });
|
|
109
|
+
Object.defineProperty(exports, "getProjectThemeDetails", { enumerable: true, get: function () { return migrateThemes_1.getProjectThemeDetails; } });
|
package/src/lib/constants.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export declare const ThemeKey = "theme";
|
|
|
3
3
|
export declare const ReactThemeKey = "react-theme";
|
|
4
4
|
export declare const AppEventsKey = "app-event";
|
|
5
5
|
export declare const AppFunctionsKey = "app-function";
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const PagesKey = "page";
|
|
7
7
|
export declare const AppObjectKey = "app-object";
|
|
8
8
|
export declare const AppObjectAssociationKey = "app-object-association";
|
|
9
9
|
export declare const CallingKey = "calling";
|
package/src/lib/constants.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.allowedSubComponentDirectories = exports.allowedComponentDirectories = exports.ProjectStructure = exports.allowedReactThemeSubComponentsDirs = exports.allowedThemeSubComponentsDirs = exports.allowedAppSubComponentsDirs = exports.packageLockJson = exports.packageJson = exports.hsProjectJsonFilename = exports.profileFilePrefix = exports.metafileExtension = exports.internalTypeToUserFacing = exports.userFacingToInternalType = exports.Components = exports.AutoGeneratedComponentTypes = exports.AppFunctionsPackageKey = exports.WorkflowActionsKey = exports.WebhooksKey = exports.VideoConferencingKey = exports.TelemetryKey = exports.SettingsKey = exports.MediaBridgeKey = exports.MCPRegistryKey = exports.MarketingEventsKey = exports.SCIMKey = exports.CardsKey = exports.CallingKey = exports.AppObjectAssociationKey = exports.AppObjectKey = exports.
|
|
36
|
+
exports.allowedSubComponentDirectories = exports.allowedComponentDirectories = exports.ProjectStructure = exports.allowedReactThemeSubComponentsDirs = exports.allowedThemeSubComponentsDirs = exports.allowedAppSubComponentsDirs = exports.packageLockJson = exports.packageJson = exports.hsProjectJsonFilename = exports.profileFilePrefix = exports.metafileExtension = exports.internalTypeToUserFacing = exports.userFacingToInternalType = exports.Components = exports.AutoGeneratedComponentTypes = exports.AppFunctionsPackageKey = exports.WorkflowActionsKey = exports.WebhooksKey = exports.VideoConferencingKey = exports.TelemetryKey = exports.SettingsKey = exports.MediaBridgeKey = exports.MCPRegistryKey = exports.MarketingEventsKey = exports.SCIMKey = exports.CardsKey = exports.CallingKey = exports.AppObjectAssociationKey = exports.AppObjectKey = exports.PagesKey = exports.AppFunctionsKey = exports.AppEventsKey = exports.ReactThemeKey = exports.ThemeKey = exports.AppKey = void 0;
|
|
37
37
|
// Top Level Component types
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
// Component types
|
|
@@ -43,7 +43,7 @@ exports.ReactThemeKey = 'react-theme';
|
|
|
43
43
|
// Sub-Component types
|
|
44
44
|
exports.AppEventsKey = 'app-event';
|
|
45
45
|
exports.AppFunctionsKey = 'app-function';
|
|
46
|
-
exports.
|
|
46
|
+
exports.PagesKey = 'page';
|
|
47
47
|
exports.AppObjectKey = 'app-object';
|
|
48
48
|
exports.AppObjectAssociationKey = 'app-object-association';
|
|
49
49
|
exports.CallingKey = 'calling';
|
|
@@ -89,11 +89,11 @@ exports.Components = {
|
|
|
89
89
|
...SubComponentFields,
|
|
90
90
|
userFriendlyName: 'App Object Association',
|
|
91
91
|
},
|
|
92
|
-
[exports.
|
|
93
|
-
dir:
|
|
92
|
+
[exports.PagesKey]: {
|
|
93
|
+
dir: 'pages',
|
|
94
94
|
parentComponent: exports.AppKey,
|
|
95
95
|
...SubComponentFields,
|
|
96
|
-
userFriendlyName: '
|
|
96
|
+
userFriendlyName: 'Page',
|
|
97
97
|
singularComponent: true,
|
|
98
98
|
},
|
|
99
99
|
[exports.ThemeKey]: {
|
package/src/lib/migrate.js
CHANGED
|
@@ -10,6 +10,7 @@ const fs_2 = __importDefault(require("fs"));
|
|
|
10
10
|
const transform_1 = require("./transform");
|
|
11
11
|
const constants_1 = require("./constants");
|
|
12
12
|
const index_1 = require("../index");
|
|
13
|
+
const utils_1 = require("./utils");
|
|
13
14
|
const IR_FILENAME = 'ir.json';
|
|
14
15
|
const filesDirectory = 'files';
|
|
15
16
|
const OUTPUT_IR_FILE = 'FULL_IR.json';
|
|
@@ -23,7 +24,7 @@ async function migrate(inputDir, outputDir) {
|
|
|
23
24
|
}
|
|
24
25
|
let hsProjectJson;
|
|
25
26
|
try {
|
|
26
|
-
hsProjectJson = loadJsonFile(hsProjectJsonPath);
|
|
27
|
+
hsProjectJson = (0, utils_1.loadJsonFile)(hsProjectJsonPath);
|
|
27
28
|
}
|
|
28
29
|
catch (e) {
|
|
29
30
|
throw new Error(`Error parsing ${constants_1.hsProjectJsonFilename}: ${e}`);
|
|
@@ -38,7 +39,7 @@ async function migrate(inputDir, outputDir) {
|
|
|
38
39
|
return;
|
|
39
40
|
}
|
|
40
41
|
const irDirName = path_1.default.dirname(filename);
|
|
41
|
-
const IR = loadJsonFile(filename);
|
|
42
|
+
const IR = (0, utils_1.loadJsonFile)(filename);
|
|
42
43
|
const { metaFilePath } = IR;
|
|
43
44
|
const projectConfig = convertIRToProjectConfig(IR);
|
|
44
45
|
const fullOutputPath = path_1.default.join(sourceCodeOutputDir, getTargetDirectoryFromComponentType(projectConfig.type));
|
|
@@ -71,11 +72,6 @@ function isIRFile(filename) {
|
|
|
71
72
|
const { base } = path_1.default.parse(filename);
|
|
72
73
|
return base.toLowerCase() === IR_FILENAME;
|
|
73
74
|
}
|
|
74
|
-
function loadJsonFile(filename) {
|
|
75
|
-
return JSON.parse(fs_2.default.readFileSync(filename, {
|
|
76
|
-
encoding: 'utf-8',
|
|
77
|
-
}));
|
|
78
|
-
}
|
|
79
75
|
function convertIRToProjectConfig(IR) {
|
|
80
76
|
const { config, uid, componentType } = IR;
|
|
81
77
|
return {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type ThemeDetails = {
|
|
2
|
+
configFilepath: string;
|
|
3
|
+
themePath: string;
|
|
4
|
+
themeConfig: {
|
|
5
|
+
secret_names?: string[];
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
type ReactThemeDetails = {
|
|
9
|
+
configFilepath: string;
|
|
10
|
+
themePath: string;
|
|
11
|
+
themeConfig: {
|
|
12
|
+
secretNames?: string[];
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
type ProjectThemeDetails = {
|
|
16
|
+
legacyThemeDetails: ThemeDetails[];
|
|
17
|
+
legacyReactThemeDetails: ReactThemeDetails[];
|
|
18
|
+
};
|
|
19
|
+
export declare function getProjectThemeDetails(projectSourceDir: string): Promise<ProjectThemeDetails>;
|
|
20
|
+
export declare function migrateThemes(projectDir: string, projectSourceDir: string): Promise<{
|
|
21
|
+
legacyThemeDetails: ThemeDetails[];
|
|
22
|
+
legacyReactThemeDetails: ReactThemeDetails[];
|
|
23
|
+
migrated: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
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.getProjectThemeDetails = getProjectThemeDetails;
|
|
7
|
+
exports.migrateThemes = migrateThemes;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const util_1 = require("util");
|
|
12
|
+
const fs_2 = require("@hubspot/local-dev-lib/fs");
|
|
13
|
+
const constants_1 = require("./constants");
|
|
14
|
+
const utils_1 = require("./utils");
|
|
15
|
+
const mkdtempAsync = (0, util_1.promisify)(fs_1.default.mkdtemp);
|
|
16
|
+
const LEGACY_THEME_CONFIG = 'theme.json';
|
|
17
|
+
const LEGACY_REACT_THEME_CONFIG = 'cms-assets.json';
|
|
18
|
+
async function getProjectThemeDetails(projectSourceDir) {
|
|
19
|
+
const files = await (0, fs_2.walk)(projectSourceDir, ['node_modules']);
|
|
20
|
+
const legacyThemeDetails = [];
|
|
21
|
+
const legacyReactThemeDetails = [];
|
|
22
|
+
files.forEach((filename) => {
|
|
23
|
+
const isLegacyThemeConfig = filename.endsWith(LEGACY_THEME_CONFIG);
|
|
24
|
+
const isLegacyReactThemeConfig = filename.endsWith(LEGACY_REACT_THEME_CONFIG);
|
|
25
|
+
if (isLegacyThemeConfig || isLegacyReactThemeConfig) {
|
|
26
|
+
const parsedConfig = (0, utils_1.loadJsonFile)(filename);
|
|
27
|
+
const themeDetails = {
|
|
28
|
+
configFilepath: filename,
|
|
29
|
+
themePath: path_1.default.dirname(filename),
|
|
30
|
+
themeConfig: parsedConfig,
|
|
31
|
+
};
|
|
32
|
+
if (isLegacyThemeConfig) {
|
|
33
|
+
legacyThemeDetails.push(themeDetails);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
legacyReactThemeDetails.push(themeDetails);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
legacyThemeDetails,
|
|
42
|
+
legacyReactThemeDetails,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function buildNewTheme(componentKey, migratedProjectTempDir, themeDetails) {
|
|
46
|
+
const themeName = path_1.default.basename(themeDetails.themePath);
|
|
47
|
+
const componentDir = path_1.default.join(migratedProjectTempDir, componentKey);
|
|
48
|
+
const newThemeDir = path_1.default.join(componentDir, themeName);
|
|
49
|
+
fs_1.default.cpSync(themeDetails.themePath, newThemeDir, {
|
|
50
|
+
recursive: true,
|
|
51
|
+
});
|
|
52
|
+
const newThemeConfig = {
|
|
53
|
+
uid: themeName,
|
|
54
|
+
type: componentKey,
|
|
55
|
+
config: {
|
|
56
|
+
themePath: themeName,
|
|
57
|
+
secretNames: [],
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
// Remove secrets from the legacy theme config if they exist & move them to the new theme config
|
|
61
|
+
let updatedLegacyThemeConfig;
|
|
62
|
+
if ('secret_names' in themeDetails.themeConfig) {
|
|
63
|
+
const { secret_names, ...configWithoutSecrets } = themeDetails.themeConfig;
|
|
64
|
+
if (secret_names) {
|
|
65
|
+
newThemeConfig.config.secretNames = secret_names;
|
|
66
|
+
updatedLegacyThemeConfig = configWithoutSecrets;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if ('secretNames' in themeDetails.themeConfig) {
|
|
70
|
+
const { secretNames, ...configWithoutSecrets } = themeDetails.themeConfig;
|
|
71
|
+
if (secretNames) {
|
|
72
|
+
newThemeConfig.config.secretNames = secretNames;
|
|
73
|
+
updatedLegacyThemeConfig = configWithoutSecrets;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (updatedLegacyThemeConfig) {
|
|
77
|
+
const newLegacyConfigFilepath = path_1.default.join(newThemeDir, path_1.default.basename(themeDetails.configFilepath));
|
|
78
|
+
fs_1.default.writeFileSync(newLegacyConfigFilepath, JSON.stringify(updatedLegacyThemeConfig, null, 2));
|
|
79
|
+
}
|
|
80
|
+
fs_1.default.writeFileSync(path_1.default.join(componentDir, `${themeName}${constants_1.metafileExtension}`), JSON.stringify(newThemeConfig, null, 2));
|
|
81
|
+
}
|
|
82
|
+
async function migrateThemes(projectDir, projectSourceDir) {
|
|
83
|
+
const { legacyThemeDetails, legacyReactThemeDetails, } = await getProjectThemeDetails(projectSourceDir);
|
|
84
|
+
// Migrate the project to a temporary directory to avoid file conflicts in the original project
|
|
85
|
+
const migratedProjectTempDir = await mkdtempAsync(path_1.default.join(os_1.default.tmpdir(), 'hubspot-migrated-project-'));
|
|
86
|
+
legacyThemeDetails.forEach(themeDetails => {
|
|
87
|
+
buildNewTheme(constants_1.ThemeKey, migratedProjectTempDir, themeDetails);
|
|
88
|
+
});
|
|
89
|
+
legacyReactThemeDetails.forEach(themeDetails => {
|
|
90
|
+
buildNewTheme(constants_1.ReactThemeKey, migratedProjectTempDir, themeDetails);
|
|
91
|
+
});
|
|
92
|
+
// Copy the rest of the project files to the temp directory
|
|
93
|
+
fs_1.default.cpSync(projectSourceDir, migratedProjectTempDir, {
|
|
94
|
+
recursive: true,
|
|
95
|
+
filter: src => {
|
|
96
|
+
return (!legacyThemeDetails.some(file => src.includes(file.themePath)) &&
|
|
97
|
+
!legacyReactThemeDetails.some(file => src.includes(file.themePath)));
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
// Archive the legacy theme files
|
|
101
|
+
const archiveDest = path_1.default.join(projectDir, 'archive');
|
|
102
|
+
fs_1.default.renameSync(projectSourceDir, archiveDest);
|
|
103
|
+
// Copy the new theme config files to the project source directory
|
|
104
|
+
fs_1.default.cpSync(migratedProjectTempDir, projectSourceDir, {
|
|
105
|
+
recursive: true,
|
|
106
|
+
});
|
|
107
|
+
fs_1.default.rmSync(migratedProjectTempDir, { recursive: true });
|
|
108
|
+
return {
|
|
109
|
+
legacyThemeDetails,
|
|
110
|
+
legacyReactThemeDetails,
|
|
111
|
+
migrated: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
package/src/lib/utils.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function loadJsonFile(filename: string): any;
|
package/src/lib/utils.js
CHANGED
|
@@ -1,29 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
object2 === null ||
|
|
10
|
-
typeof object1 !== 'object' ||
|
|
11
|
-
typeof object2 !== 'object') {
|
|
12
|
-
return object1 === object2;
|
|
13
|
-
}
|
|
14
|
-
if (typeof object1 !== typeof object2) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
const objKeys1 = Object.keys(object1);
|
|
18
|
-
const objKeys2 = Object.keys(object2);
|
|
19
|
-
if (objKeys1.length !== objKeys2.length)
|
|
20
|
-
return false;
|
|
21
|
-
for (const key of objKeys1) {
|
|
22
|
-
const value1 = object1[key];
|
|
23
|
-
const value2 = object2[key];
|
|
24
|
-
if (!isDeepEqual(value1, value2)) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return true;
|
|
6
|
+
exports.loadJsonFile = loadJsonFile;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
function loadJsonFile(filename) {
|
|
9
|
+
return JSON.parse(fs_1.default.readFileSync(filename, {
|
|
10
|
+
encoding: 'utf-8',
|
|
11
|
+
}));
|
|
29
12
|
}
|