Skip to content

Commit 7729865

Browse files
committed
eslint rule update
1 parent 87fcd5d commit 7729865

File tree

122 files changed

+280
-167
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

122 files changed

+280
-167
lines changed

eslint-local-rules/require-platform-declaration.js

Lines changed: 111 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,97 @@
11
/**
22
* ESLint Rule: require-platform-declaration
33
*
4-
* Ensures that all non-test source files export __supportedPlatforms
4+
* Ensures that all non-test source files export __supportedPlatforms with valid platform values
55
*
66
* Valid:
7-
* export const __supportedPlatforms = ['browser'] as const;
8-
* export const __supportedPlatforms = ['__universal__'] as const;
9-
* export const __supportedPlatforms: Platform[] = ['browser', 'node'];
7+
* export const __supportedPlatforms = ['browser'];
8+
* export const __supportedPlatforms = ['__universal__'];
9+
* export const __supportedPlatforms = ['browser', 'node'];
1010
*
1111
* Invalid:
1212
* // Missing __supportedPlatforms export
13+
* // Invalid platform values (must match Platform type definition in platform_support.ts)
14+
* // Not exported as const array
1315
*/
1416

17+
const fs = require('fs');
18+
const path = require('path');
19+
const ts = require('typescript');
20+
21+
// Cache for valid platforms
22+
let validPlatformsCache = null;
23+
24+
function getValidPlatforms(context) {
25+
if (validPlatformsCache) {
26+
return validPlatformsCache;
27+
}
28+
29+
try {
30+
const filename = context.getFilename();
31+
const workspaceRoot = filename.split('/lib/')[0];
32+
const platformSupportPath = path.join(workspaceRoot, 'lib', 'platform_support.ts');
33+
34+
if (fs.existsSync(platformSupportPath)) {
35+
const content = fs.readFileSync(platformSupportPath, 'utf8');
36+
const sourceFile = ts.createSourceFile(
37+
platformSupportPath,
38+
content,
39+
ts.ScriptTarget.Latest,
40+
true
41+
);
42+
43+
const platforms = [];
44+
45+
// Visit all nodes in the AST
46+
function visit(node) {
47+
// Look for: export type Platform = 'browser' | 'node' | ...
48+
if (ts.isTypeAliasDeclaration(node) &&
49+
node.name.text === 'Platform' &&
50+
node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
51+
52+
// Parse the union type
53+
if (ts.isUnionTypeNode(node.type)) {
54+
for (const type of node.type.types) {
55+
if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) {
56+
platforms.push(type.literal.text);
57+
}
58+
}
59+
}
60+
}
61+
62+
ts.forEachChild(node, visit);
63+
}
64+
65+
visit(sourceFile);
66+
67+
if (platforms.length > 0) {
68+
validPlatformsCache = platforms;
69+
return validPlatformsCache;
70+
}
71+
}
72+
} catch (error) {
73+
// Fallback to hardcoded values if parsing fails
74+
console.warn('Could not parse platform_support.ts, using fallback values:', error.message);
75+
}
76+
77+
// Fallback to default platforms
78+
validPlatformsCache = ['browser', 'node', 'react_native', '__universal__'];
79+
return validPlatformsCache;
80+
}
81+
1582
module.exports = {
1683
meta: {
1784
type: 'problem',
1885
docs: {
19-
description: 'Require __supportedPlatforms export in all source files',
86+
description: 'Require __supportedPlatforms export with valid platform values in all source files',
2087
category: 'Best Practices',
2188
recommended: true,
2289
},
2390
messages: {
24-
missingPlatformDeclaration: 'File must export __supportedPlatforms to declare which platforms it supports. Example: export const __supportedPlatforms = [\'__universal__\'] as const;',
25-
invalidPlatformDeclaration: '__supportedPlatforms must be exported as a const array. Example: export const __supportedPlatforms = [\'browser\', \'node\'] as const;',
91+
missingPlatformDeclaration: 'File must export __supportedPlatforms to declare which platforms it supports. Example: export const __supportedPlatforms = [\'__universal__\'];',
92+
invalidPlatformDeclaration: '__supportedPlatforms must be exported as a const array. Example: export const __supportedPlatforms = [\'browser\', \'node\'];',
93+
invalidPlatformValue: '__supportedPlatforms contains invalid platform value "{{value}}". Valid platforms are: {{validPlatforms}}',
94+
emptyPlatformArray: '__supportedPlatforms array cannot be empty. Specify at least one platform or use [\'__universal__\']',
2695
},
2796
schema: [],
2897
},
@@ -49,8 +118,8 @@ module.exports = {
49118
return {};
50119
}
51120

121+
const VALID_PLATFORMS = getValidPlatforms(context);
52122
let hasPlatformExport = false;
53-
let isValidExport = false;
54123

55124
return {
56125
ExportNamedDeclaration(node) {
@@ -73,7 +142,7 @@ module.exports = {
73142
return;
74143
}
75144

76-
// Validate it's an array
145+
// Validate it's an array expression
77146
let init = declarator.init;
78147

79148
// Handle TSAsExpression: [...] as const
@@ -86,13 +155,43 @@ module.exports = {
86155
init = init.expression;
87156
}
88157

89-
if (init && init.type === 'ArrayExpression') {
90-
isValidExport = true;
91-
} else {
158+
if (!init || init.type !== 'ArrayExpression') {
92159
context.report({
93160
node: declarator,
94161
messageId: 'invalidPlatformDeclaration',
95162
});
163+
return;
164+
}
165+
166+
// Check if array is empty
167+
if (init.elements.length === 0) {
168+
context.report({
169+
node: init,
170+
messageId: 'emptyPlatformArray',
171+
});
172+
return;
173+
}
174+
175+
// Validate each array element is a valid platform string
176+
for (const element of init.elements) {
177+
if (element && element.type === 'Literal' && typeof element.value === 'string') {
178+
if (!VALID_PLATFORMS.includes(element.value)) {
179+
context.report({
180+
node: element,
181+
messageId: 'invalidPlatformValue',
182+
data: {
183+
value: element.value,
184+
validPlatforms: VALID_PLATFORMS.map(p => `'${p}'`).join(', ')
185+
}
186+
});
187+
}
188+
} else {
189+
// Not a string literal
190+
context.report({
191+
node: element || init,
192+
messageId: 'invalidPlatformDeclaration',
193+
});
194+
}
96195
}
97196
}
98197
}

lib/client_factory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { InMemoryLruCache } from "./utils/cache/in_memory_lru_cache";
2929
import { transformCache, CacheWithRemove } from "./utils/cache/cache";
3030
import { ConstantBackoff } from "./utils/repeater/repeater";
3131

32-
export const __supportedPlatforms = ['__universal__'] as const;
32+
export const __supportedPlatforms = ['__universal__'];
3333

3434
export type OptimizelyFactoryConfig = Config & {
3535
requestHandler: RequestHandler;

lib/common_exports.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
export const __supportedPlatforms = ['__universal__'] as const;
17+
export const __supportedPlatforms = ['__universal__'];
1818

1919
export { createStaticProjectConfigManager } from './project_config/config_manager_factory';
2020

lib/core/audience_evaluator/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { CONDITION_EVALUATOR_ERROR, UNKNOWN_CONDITION_TYPE } from 'error_message
2121
import { AUDIENCE_EVALUATION_RESULT, EVALUATING_AUDIENCE} from 'log_message';
2222
import { LoggerFacade } from '../../logging/logger';
2323

24-
export const __supportedPlatforms = ['__universal__'] as const;
24+
export const __supportedPlatforms = ['__universal__'];
2525

2626
export class AudienceEvaluator {
2727
private logger?: LoggerFacade;

lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { UNKNOWN_MATCH_TYPE } from 'error_message';
1717
import { LoggerFacade } from '../../../logging/logger';
1818
import { Condition, OptimizelyUserContext } from '../../../shared_types';
1919

20-
export const __supportedPlatforms = ['__universal__'] as const;
20+
export const __supportedPlatforms = ['__universal__'];
2121

2222
const QUALIFIED_MATCH_TYPE = 'qualified';
2323

lib/core/bucketer/bucket_value_generator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import murmurhash from 'murmurhash';
1717
import { INVALID_BUCKETING_ID } from 'error_message';
1818
import { OptimizelyError } from '../../error/optimizly_error';
1919

20-
export const __supportedPlatforms = ['__universal__'] as const;
20+
export const __supportedPlatforms = ['__universal__'];
2121

2222
const HASH_SEED = 1;
2323
const MAX_HASH_VALUE = Math.pow(2, 32);

lib/core/bucketer/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { OptimizelyError } from '../../error/optimizly_error';
2929
import { generateBucketValue } from './bucket_value_generator';
3030
import { DecisionReason } from '../decision_service';
3131

32-
export const __supportedPlatforms = ['__universal__'] as const;
32+
export const __supportedPlatforms = ['__universal__'];
3333

3434
export const USER_NOT_IN_ANY_EXPERIMENT = 'User %s is not in any experiment of group %s.';
3535
export const USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP = 'User %s is not in experiment %s of group %s.';

lib/core/condition_tree_evaluator/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License. *
1515
***************************************************************************/
1616

17-
export const __supportedPlatforms = ['__universal__'] as const;
17+
export const __supportedPlatforms = ['__universal__'];
1818

1919
const AND_CONDITION = 'and';
2020
const OR_CONDITION = 'or';

lib/core/decision/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { DecisionObj } from '../decision_service';
2121
* @param {DecisionObj} decisionObj Object representing decision
2222
* @returns {string} Experiment key or empty string if experiment is null
2323
*/
24-
export const __supportedPlatforms = ['__universal__'] as const;
24+
export const __supportedPlatforms = ['__universal__'];
2525

2626

2727
export function getExperimentKey(decisionObj: DecisionObj): string {

lib/core/decision_service/cmab/cmab_client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { isSuccessStatusCode } from "../../../utils/http_request_handler/http_ut
2424
import { BackoffController } from "../../../utils/repeater/repeater";
2525
import { Producer } from "../../../utils/type";
2626

27-
export const __supportedPlatforms = ['__universal__'] as const;
27+
export const __supportedPlatforms = ['__universal__'];
2828

2929
export interface CmabClient {
3030
fetchDecision(

0 commit comments

Comments
 (0)