@hubspot/ui-extensions-dev-server 0.8.48 → 0.8.52

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.
@@ -44,6 +44,8 @@ class DevModeParentInterface {
44
44
  return constants_1.PLATFORM_VERSION.V20231;
45
45
  case constants_1.PLATFORM_VERSION.V20232:
46
46
  return constants_1.PLATFORM_VERSION.V20232;
47
+ case constants_1.PLATFORM_VERSION.V20251:
48
+ return constants_1.PLATFORM_VERSION.V20251;
47
49
  case constants_1.PLATFORM_VERSION.V20252:
48
50
  case constants_1.PLATFORM_VERSION.UNSTABLE:
49
51
  return constants_1.PLATFORM_VERSION.V20252;
@@ -65,6 +65,10 @@ class DevModeUnifiedInterface extends DevModeParentInterface_1.DevModeParentInte
65
65
  const extension = components[extensionUid];
66
66
  // Update the extension entrypoint to be "relative" to the extension directory (eg from /app/card/card.jsx to ./card.jsx)
67
67
  extension.config.entrypoint = `./${path_1.default.basename(extension.config.entrypoint)}`;
68
+ // Hardcode the extension name if this is a settings extension (the user does not provide a name for their settings card).
69
+ if (extension.componentType === types_1.UnifiedComponentTypes.SETTINGS) {
70
+ extension.config.name = 'Settings';
71
+ }
68
72
  // Add them to the app config
69
73
  switch (extension.componentType) {
70
74
  case types_1.UnifiedComponentTypes.CARD:
package/dist/lib/ast.d.ts CHANGED
@@ -8,5 +8,8 @@ export declare function checkForOutOfBoundsImageImports(node: Node, output: Sour
8
8
  export declare function traverseAbstractSyntaxTree(ast: Program, checks: SourceCodeChecks, extensionPath: string, logger: Logger): {
9
9
  functions: {};
10
10
  badImports: never[];
11
- dataDependencies: never[];
11
+ dataDependencies: {
12
+ importedHooks: {};
13
+ dependencies: never[];
14
+ };
12
15
  };
package/dist/lib/ast.js CHANGED
@@ -9,6 +9,7 @@ const path_1 = __importDefault(require("path"));
9
9
  // @ts-expect-error no type defs
10
10
  const estraverse_1 = require("estraverse");
11
11
  const utils_1 = require("./utils");
12
+ const EXPERIMENTAL_HOOKS = ['useCrmProperties'];
12
13
  function _isVariableImported(node, variableName) {
13
14
  if (!node) {
14
15
  return false;
@@ -84,40 +85,93 @@ function _collectDataDependencies(node, output, logger) {
84
85
  return;
85
86
  }
86
87
  try {
87
- // For now we only support `useCrmProperties`, but we can generalize this in the future.
88
- if (node.type === 'CallExpression' &&
89
- node.callee.type === 'Identifier' &&
90
- node.callee.name === 'useCrmProperties') {
91
- const propertyType = 'CrmRecordProperties';
92
- // Get the second argument, the properties array.
93
- const propertiesNode = node.arguments[1];
94
- const requestedProperties = [];
95
- // If the second argument is an array with at least one element, we will collect the properties.
96
- if (propertiesNode &&
97
- propertiesNode.type === 'ArrayExpression' &&
98
- propertiesNode.elements.length > 0) {
99
- propertiesNode.elements.forEach((element) => {
100
- /**
101
- * We only support strings for now, and ignore the rest.
102
- * Again, this might be more generalized in the future as we support more hooks.
103
- */
104
- if (element &&
105
- element.type === 'Literal' &&
106
- typeof element.value === 'string') {
107
- requestedProperties.push(element.value);
88
+ // Check for imports of our hooks.
89
+ if (node.type === 'ImportDeclaration' &&
90
+ typeof node.source.value === 'string' &&
91
+ node.source.value.startsWith('@hubspot/ui-extensions')) {
92
+ const isExperimental = node.source.value === '@hubspot/ui-extensions/experimental';
93
+ // If the imports are coming from our own package, loop over them to check for hooks
94
+ node.specifiers.forEach((specifier) => {
95
+ // If the specifier is an ImportSpecifier and the imported name is one of our tracked hooks, we will track it.
96
+ if (specifier.type === 'ImportSpecifier' &&
97
+ isExperimental &&
98
+ EXPERIMENTAL_HOOKS.includes(specifier.imported.name)) {
99
+ // The local name is the name the hook is imported as in the file, and the imported name is the original name of the hook.
100
+ output.dataDependencies.importedHooks[`${specifier.local.name}`] =
101
+ specifier.imported.name;
102
+ }
103
+ else if (
104
+ // We also have to track namespace level imports
105
+ specifier.type === 'ImportNamespaceSpecifier') {
106
+ // If the specifier is a namespace import, we will track all hooks that are imported from that namespace. Experimental and stable hooks are handled separately.
107
+ if (isExperimental) {
108
+ EXPERIMENTAL_HOOKS.forEach((hook) => {
109
+ output.dataDependencies.importedHooks[`${specifier.local.name}.${hook}`] = hook;
110
+ });
108
111
  }
109
- });
110
- output.dataDependencies.push({
111
- /**
112
- * This refID is a hash of the property type and the requested properties.
113
- * This should allow us to create the same hash at execution time, to find the correct data from BE.
114
- */
115
- referenceId: (0, utils_1.generateHash)(propertyType, requestedProperties),
116
- properties: {
117
- type: propertyType,
118
- recordProperties: requestedProperties,
119
- },
120
- });
112
+ }
113
+ });
114
+ // Check for calls to our hooks.
115
+ }
116
+ else if (node.type === 'CallExpression') {
117
+ let hookName = '';
118
+ // This handles the case where the hook is called directly, eg `useCrmProperties()` (also supports aliased hooks)
119
+ if (node.callee.type === 'Identifier' &&
120
+ output.dataDependencies.importedHooks[node.callee.name]) {
121
+ hookName = output.dataDependencies.importedHooks[node.callee.name];
122
+ }
123
+ else if (
124
+ // This handles namespace import usage, eg `uiExtensions.useCrmProperties()`
125
+ node.callee.type === 'MemberExpression' &&
126
+ node.callee.object.type === 'Identifier' &&
127
+ node.callee.property.type === 'Identifier' &&
128
+ output.dataDependencies.importedHooks[`${node.callee.object.name}.${node.callee.property.name}`]) {
129
+ hookName =
130
+ output.dataDependencies.importedHooks[`${node.callee.object.name}.${node.callee.property.name}`];
131
+ }
132
+ // Then we handle each hook individually, as the usages and tracking format are different.
133
+ if (hookName === 'useCrmProperties') {
134
+ const propertyType = 'CrmRecordProperties';
135
+ // Validate actions parameter
136
+ if (!node.arguments[0]) {
137
+ logger.warn('useCrmProperties called without actions parameter');
138
+ return;
139
+ }
140
+ // Get the second argument, the properties array
141
+ const propertiesNode = node.arguments[1];
142
+ const requestedProperties = [];
143
+ // If the second argument is an array with at least one element, collect the properties.
144
+ if (propertiesNode &&
145
+ propertiesNode.type === 'ArrayExpression' &&
146
+ propertiesNode.elements.length > 0) {
147
+ propertiesNode.elements.forEach((element) => {
148
+ /**
149
+ * We only support strings for now, and ignore the rest.
150
+ * This might be more generalized in the future as we support more hooks.
151
+ */
152
+ if (element &&
153
+ element.type === 'Literal' &&
154
+ typeof element.value === 'string') {
155
+ requestedProperties.push(element.value);
156
+ }
157
+ else {
158
+ logger.warn(`Invalid property type in useCrmProperties: ${element ? element.type : 'undefined'}`);
159
+ }
160
+ });
161
+ if (requestedProperties.length > 0) {
162
+ output.dataDependencies.dependencies.push({
163
+ /**
164
+ * This refID is a hash of the property type and the requested properties.
165
+ * This should allow us to create the same hash at execution time, to find the correct data from BE.
166
+ */
167
+ referenceId: (0, utils_1.generateHash)(propertyType, requestedProperties),
168
+ properties: {
169
+ type: propertyType,
170
+ recordProperties: requestedProperties,
171
+ },
172
+ });
173
+ }
174
+ }
121
175
  }
122
176
  }
123
177
  }
@@ -131,7 +185,10 @@ function traverseAbstractSyntaxTree(ast, checks, extensionPath, logger) {
131
185
  const state = {
132
186
  functions: {},
133
187
  badImports: [],
134
- dataDependencies: [],
188
+ dataDependencies: {
189
+ importedHooks: {},
190
+ dependencies: [],
191
+ },
135
192
  };
136
193
  try {
137
194
  (0, estraverse_1.traverse)(ast, {
@@ -21,11 +21,13 @@ export declare const SERVER_CAPABILITIES: string[];
21
21
  export declare const PLATFORM_VERSION: {
22
22
  readonly V20231: "2023.1";
23
23
  readonly V20232: "2023.2";
24
+ readonly V20251: "2025.1";
24
25
  readonly V20252: "2025.2";
25
26
  readonly UNSTABLE: "unstable";
26
27
  };
27
28
  export declare const PUBLIC_APP = "public-app";
28
29
  export declare const PRIVATE_APP = "private-app";
29
30
  export declare const CARD_EXTENSION = "CARD";
31
+ export declare const SETTINGS_EXTENSION = "SETTINGS";
30
32
  export declare const SUPPORTED_APP_TYPES: string[];
31
33
  export declare const SUPPORTED_EXTENSION_TYPES: string[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SUPPORTED_EXTENSION_TYPES = exports.SUPPORTED_APP_TYPES = exports.CARD_EXTENSION = exports.PRIVATE_APP = exports.PUBLIC_APP = exports.PLATFORM_VERSION = exports.SERVER_CAPABILITIES = exports.PROXY_CAPABILITY = exports.WEBSOCKET_MESSAGE_VERSION = exports.EXTENSIONS_MESSAGE_VERSION = exports.ROLLUP_OPTIONS = exports.WEBSOCKET_DEFAULT_PORT = exports.EXPRESS_DEFAULT_PORT = exports.VITE_DEV_SERVER_ID = exports.EXPRESS_SERVER_ID = exports.MANIFEST_FILE = exports.OUTPUT_DIR = void 0;
3
+ exports.SUPPORTED_EXTENSION_TYPES = exports.SUPPORTED_APP_TYPES = exports.SETTINGS_EXTENSION = exports.CARD_EXTENSION = exports.PRIVATE_APP = exports.PUBLIC_APP = exports.PLATFORM_VERSION = exports.SERVER_CAPABILITIES = exports.PROXY_CAPABILITY = exports.WEBSOCKET_MESSAGE_VERSION = exports.EXTENSIONS_MESSAGE_VERSION = exports.ROLLUP_OPTIONS = exports.WEBSOCKET_DEFAULT_PORT = exports.EXPRESS_DEFAULT_PORT = exports.VITE_DEV_SERVER_ID = exports.EXPRESS_SERVER_ID = exports.MANIFEST_FILE = exports.OUTPUT_DIR = void 0;
4
4
  exports.OUTPUT_DIR = 'dist';
5
5
  exports.MANIFEST_FILE = 'manifest.json';
6
6
  exports.EXPRESS_SERVER_ID = 'ui-extensions-dev-server';
@@ -31,6 +31,7 @@ exports.SERVER_CAPABILITIES = [
31
31
  exports.PLATFORM_VERSION = {
32
32
  V20231: '2023.1',
33
33
  V20232: '2023.2',
34
+ V20251: '2025.1',
34
35
  V20252: '2025.2',
35
36
  UNSTABLE: 'unstable',
36
37
  };
@@ -39,5 +40,6 @@ exports.PUBLIC_APP = 'public-app';
39
40
  // eslint-disable-next-line hubspot-dev/no-private-classes
40
41
  exports.PRIVATE_APP = 'private-app';
41
42
  exports.CARD_EXTENSION = 'CARD';
43
+ exports.SETTINGS_EXTENSION = 'SETTINGS';
42
44
  exports.SUPPORTED_APP_TYPES = [exports.PUBLIC_APP, exports.PRIVATE_APP];
43
- exports.SUPPORTED_EXTENSION_TYPES = [exports.CARD_EXTENSION];
45
+ exports.SUPPORTED_EXTENSION_TYPES = [exports.CARD_EXTENSION, exports.SETTINGS_EXTENSION];
@@ -14,7 +14,10 @@ const codeBlockingPlugin = ({ logger, extensionPath }) => {
14
14
  let sourceCodeMetadata = {
15
15
  functions: {},
16
16
  badImports: [],
17
- dataDependencies: [],
17
+ dataDependencies: {
18
+ importedHooks: {},
19
+ dependencies: [],
20
+ },
18
21
  };
19
22
  const requireFunctionName = 'require';
20
23
  try {
@@ -34,7 +34,7 @@ const manifestPlugin = (options) => {
34
34
  const sourceCodeMetadata = (0, ast_1.traverseAbstractSyntaxTree)(ast, [], options.extensionPath || process.cwd(), logger);
35
35
  allDataDependencies = [
36
36
  ...allDataDependencies,
37
- ...sourceCodeMetadata.dataDependencies,
37
+ ...sourceCodeMetadata.dataDependencies.dependencies,
38
38
  ];
39
39
  }
40
40
  catch (e) {
@@ -130,10 +130,11 @@ export type UnifiedCardConfig = {
130
130
  };
131
131
  export declare enum UnifiedComponentTypes {
132
132
  CARD = "CARD",
133
- APPLICATION = "APPLICATION"
133
+ APPLICATION = "APPLICATION",
134
+ SETTINGS = "SETTINGS"
134
135
  }
135
136
  export type UnifiedExtensionComponent = Omit<UnifiedCardConfig, 'type'> & {
136
- componentType: UnifiedComponentTypes.CARD;
137
+ componentType: UnifiedComponentTypes.CARD | UnifiedComponentTypes.SETTINGS;
137
138
  componentDeps: {
138
139
  app: string;
139
140
  };
@@ -209,7 +210,12 @@ export interface SourceCodeMetadata {
209
210
  [functionName: string]: FunctionMetadata;
210
211
  };
211
212
  badImports: string[];
212
- dataDependencies: DataDependency[];
213
+ dataDependencies: {
214
+ importedHooks: {
215
+ [hookName: string]: string;
216
+ };
217
+ dependencies: DataDependency[];
218
+ };
213
219
  }
214
220
  export interface FunctionInvocationCheck {
215
221
  functionName: string;
package/dist/lib/types.js CHANGED
@@ -10,4 +10,5 @@ var UnifiedComponentTypes;
10
10
  (function (UnifiedComponentTypes) {
11
11
  UnifiedComponentTypes["CARD"] = "CARD";
12
12
  UnifiedComponentTypes["APPLICATION"] = "APPLICATION";
13
+ UnifiedComponentTypes["SETTINGS"] = "SETTINGS";
13
14
  })(UnifiedComponentTypes || (exports.UnifiedComponentTypes = UnifiedComponentTypes = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/ui-extensions-dev-server",
3
- "version": "0.8.48",
3
+ "version": "0.8.52",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "uie": "./dist/lib/bin/cli.js"
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "dependencies": {
30
- "@hubspot/app-functions-dev-server": "0.8.48",
30
+ "@hubspot/app-functions-dev-server": "0.8.50",
31
31
  "chalk": "5.4.1",
32
32
  "commander": "13.0.0",
33
33
  "cors": "2.8.5",
@@ -69,5 +69,5 @@
69
69
  "optional": true
70
70
  }
71
71
  },
72
- "gitHead": "7ef1984c151f5631b056efdce2395e8b43375c90"
72
+ "gitHead": "49d7e6ce23cf06a54cbe038f11bb1b5e57358254"
73
73
  }