Skip to content

Commit 2f93e2d

Browse files
committed
finalize scripts
1 parent 901cc4a commit 2f93e2d

File tree

4 files changed

+134
-95
lines changed

4 files changed

+134
-95
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"clean": "rm -rf dist",
5858
"clean:win": "(if exist dist rd /s/q dist)",
5959
"lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'",
60-
"validate-platform-isolation": "node scripts/validate-platform-isolation-ts.js",
60+
"validate-platform-isolation": "node scripts/validate-platform-isolation.js",
6161
"test-platform-isolation": "node scripts/test-validator.js",
6262
"add-platform-exports": "node scripts/add-platform-exports.js",
6363
"test-vitest": "vitest run",

scripts/add-platform-exports.js

Lines changed: 84 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
#!/usr/bin/env node
22

3+
/**
4+
* Copyright 2025, Optimizely
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
319
/**
420
* Auto-add __platforms to files
521
*
@@ -13,6 +29,7 @@
1329
* 4. Inserts __platforms export at the end of the file
1430
*/
1531

32+
/* eslint-disable @typescript-eslint/no-var-requires */
1633
const fs = require('fs');
1734
const path = require('path');
1835
const ts = require('typescript');
@@ -84,26 +101,27 @@ function ensurePlatformImport(content, filePath) {
84101
let lastImportEnd = 0;
85102

86103
function visit(node) {
87-
if (ts.isImportDeclaration(node)) {
88-
const moduleSpecifier = node.moduleSpecifier;
89-
if (ts.isStringLiteral(moduleSpecifier)) {
90-
// Check if this import is from platform_support
91-
if (moduleSpecifier.text.includes('platform_support')) {
92-
// Check if it imports Platform type
93-
if (node.importClause && node.importClause.namedBindings) {
94-
const namedBindings = node.importClause.namedBindings;
95-
if (ts.isNamedImports(namedBindings)) {
96-
for (const element of namedBindings.elements) {
97-
if (element.name.text === 'Platform') {
98-
hasPlatformImport = true;
99-
break;
100-
}
101-
}
102-
}
103-
}
104-
}
104+
if (!ts.isImportDeclaration(node)) return;
105+
106+
lastImportEnd = node.end;
107+
108+
const moduleSpecifier = node.moduleSpecifier;
109+
if (!ts.isStringLiteral(moduleSpecifier)) return;
110+
111+
// Check if this import is from platform_support
112+
if (!moduleSpecifier.text.includes('platform_support')) return;
113+
114+
// Check if it imports Platform type
115+
if (!node.importClause?.namedBindings) return;
116+
117+
const namedBindings = node.importClause.namedBindings;
118+
if (!ts.isNamedImports(namedBindings)) return;
119+
120+
for (const element of namedBindings.elements) {
121+
if (element.name.text === 'Platform') {
122+
hasPlatformImport = true;
123+
break;
105124
}
106-
lastImportEnd = node.end;
107125
}
108126
}
109127

@@ -175,19 +193,19 @@ function isPlatformExportAtEnd(content, filePath) {
175193
}
176194

177195
// Find __platforms export
178-
if (ts.isVariableStatement(node)) {
179-
const hasExport = node.modifiers?.some(
180-
mod => mod.kind === ts.SyntaxKind.ExportKeyword
181-
);
182-
183-
if (hasExport) {
184-
for (const declaration of node.declarationList.declarations) {
185-
if (ts.isVariableDeclaration(declaration) &&
186-
ts.isIdentifier(declaration.name) &&
187-
declaration.name.text === '__platforms') {
188-
platformExportEnd = node.end;
189-
}
190-
}
196+
if (!ts.isVariableStatement(node)) return;
197+
198+
const hasExport = node.modifiers?.some(
199+
mod => mod.kind === ts.SyntaxKind.ExportKeyword
200+
);
201+
202+
if (!hasExport) return;
203+
204+
for (const declaration of node.declarationList.declarations) {
205+
if (ts.isVariableDeclaration(declaration) &&
206+
ts.isIdentifier(declaration.name) &&
207+
declaration.name.text === '__platforms') {
208+
platformExportEnd = node.end;
191209
}
192210
}
193211
}
@@ -218,40 +236,42 @@ function extractExistingPlatformExport(content, filePath) {
218236
const linesToRemove = new Set();
219237

220238
function visit(node) {
221-
if (ts.isVariableStatement(node)) {
222-
const hasExport = node.modifiers?.some(
223-
mod => mod.kind === ts.SyntaxKind.ExportKeyword
224-
);
239+
if (!ts.isVariableStatement(node)) return;
240+
241+
const hasExport = node.modifiers?.some(
242+
mod => mod.kind === ts.SyntaxKind.ExportKeyword
243+
);
244+
245+
if (!hasExport) return;
246+
247+
for (const declaration of node.declarationList.declarations) {
248+
if (!ts.isVariableDeclaration(declaration) ||
249+
!ts.isIdentifier(declaration.name) ||
250+
declaration.name.text !== '__platforms') {
251+
continue;
252+
}
225253

226-
if (hasExport) {
227-
for (const declaration of node.declarationList.declarations) {
228-
if (ts.isVariableDeclaration(declaration) &&
229-
ts.isIdentifier(declaration.name) &&
230-
declaration.name.text === '__platforms') {
231-
// Extract the full statement
232-
const startLine = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line;
233-
const endLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line;
234-
235-
const statementLines = [];
236-
for (let i = startLine; i <= endLine; i++) {
237-
linesToRemove.add(i);
238-
statementLines.push(lines[i]);
239-
}
240-
exportStatement = statementLines.join('\n');
241-
}
242-
}
254+
// Extract the full statement
255+
const startLine = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line;
256+
const endLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line;
257+
258+
const statementLines = [];
259+
for (let i = startLine; i <= endLine; i++) {
260+
linesToRemove.add(i);
261+
statementLines.push(lines[i]);
243262
}
263+
exportStatement = statementLines.join('\n');
244264
}
245265
}
246266

247267
ts.forEachChild(sourceFile, visit);
248268

249269
if (!exportStatement) {
250-
return { content, statement: null, removed: false };
270+
return { restContent: content, platformExportStatement: null };
251271
}
252272

253273
const filteredLines = lines.filter((_, index) => !linesToRemove.has(index));
254-
return { content: filteredLines.join('\n'), statement: exportStatement, removed: true };
274+
return { restContent: filteredLines.join('\n'), platformExportStatement: exportStatement };
255275
}
256276

257277
/**
@@ -288,7 +308,7 @@ function processFile(filePath) {
288308
// If file already has valid platforms, use those (preserve existing values)
289309
// Otherwise, determine from filename or default to universal
290310
let platforms;
291-
if (!existingPlatforms || needsFixing) {
311+
if (needsFixing) {
292312
// No __platforms export or has errors, determine from filename
293313
const platformsFromFilename = getPlatformFromFilename(filePath);
294314
platforms = platformsFromFilename || ['__universal__'];
@@ -303,9 +323,8 @@ function processFile(filePath) {
303323
if (needsFixing) {
304324
// Has issues (MISSING, NOT_ARRAY, NOT_LITERALS, INVALID_VALUES, etc.), fix them
305325
const extracted = extractExistingPlatformExport(content, filePath);
306-
if (extracted.removed) {
307-
content = extracted.content;
308-
}
326+
content = extracted.restContent;
327+
309328
action = 'fixed';
310329
modified = true;
311330

@@ -323,20 +342,12 @@ function processFile(filePath) {
323342

324343
// Extract it and move to end without modification
325344
const extracted = extractExistingPlatformExport(content, filePath);
326-
if (extracted.removed) {
327-
content = extracted.content;
328-
329-
// Ensure Platform import exists
330-
const importResult = ensurePlatformImport(content, filePath);
331-
content = importResult.content;
332-
333-
// Add the original statement at the end
334-
content = addPlatformExportStatement(content, extracted.statement);
335-
action = 'moved';
336-
modified = true;
337-
} else {
338-
return { skipped: true, reason: 'could not extract export' };
339-
}
345+
content = extracted.restContent;
346+
347+
// Add the original statement at the end
348+
content = addPlatformExportStatement(content, extracted.platformExportStatement);
349+
action = 'moved';
350+
modified = true;
340351
}
341352

342353
if (modified) {

scripts/test-validator.js

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
#!/usr/bin/env node
22

3+
/**
4+
* Copyright 2025, Optimizely
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
319
/**
420
* Comprehensive test suite for platform isolation validator
521
*
622
* This test documents and validates all the compatibility rules
723
*/
824

25+
26+
/* eslint-disable @typescript-eslint/no-var-requires */
927
const assert = require('assert');
1028
const validator = require('./validate-platform-isolation.js');
1129

@@ -35,19 +53,43 @@ test('Node file can import universal',
3553
validator.isPlatformCompatible(['node'], ['__universal__']), true);
3654
test('Multi-platform file can import universal',
3755
validator.isPlatformCompatible(['browser', 'react_native'], ['__universal__']), true);
56+
test('Universal file can import universal',
57+
validator.isPlatformCompatible(['__universal__'], ['__universal__']), true);
58+
3859

3960
console.log('\n2. SINGLE PLATFORM FILES');
4061
console.log('-'.repeat(70));
4162
test('Browser file can import from browser file',
4263
validator.isPlatformCompatible(['browser'], ['browser']), true);
64+
test('Browser file can import from universal file',
65+
validator.isPlatformCompatible(['browser'], ['__universal__']), true);
4366
test('Browser file CANNOT import from node file',
4467
validator.isPlatformCompatible(['browser'], ['node']), false);
4568
test('Node file can import from node file',
4669
validator.isPlatformCompatible(['node'], ['node']), true);
70+
test('Node file can import from universal file',
71+
validator.isPlatformCompatible(['node'], ['__universal__']), true);
4772
test('React Native file can import from react_native file',
4873
validator.isPlatformCompatible(['react_native'], ['react_native']), true);
74+
test('React Native file can import from universal file',
75+
validator.isPlatformCompatible(['react_native'], ['__universal__']), true);
76+
77+
78+
console.log('\n3. UNIVERSAL IMPORTING FROM NON-UNIVERSAL');
79+
console.log('-'.repeat(70));
80+
test('Universal file CANNOT import from browser file',
81+
validator.isPlatformCompatible(['__universal__'], ['browser']), false);
82+
test('Universal file CANNOT import from node file',
83+
validator.isPlatformCompatible(['__universal__'], ['node']), false);
84+
test('Universal file CANNOT import from react_native file',
85+
validator.isPlatformCompatible(['__universal__'], ['react_native']), false);
86+
test('Universal file CANNOT import from [browser, react_native] file',
87+
validator.isPlatformCompatible(['__universal__'], ['browser', 'react_native']), false);
88+
test('Universal file CANNOT import from [browser, node] file',
89+
validator.isPlatformCompatible(['__universal__'], ['browser', 'node']), false);
90+
4991

50-
console.log('\n3. SINGLE PLATFORM IMPORTING FROM MULTI-PLATFORM');
92+
console.log('\n4. SINGLE PLATFORM IMPORTING FROM MULTI-PLATFORM');
5193
console.log('-'.repeat(70));
5294
test('Browser file CAN import from [browser, react_native] file',
5395
validator.isPlatformCompatible(['browser'], ['browser', 'react_native']), true);
@@ -56,33 +98,19 @@ test('React Native file CAN import from [browser, react_native] file',
5698
test('Node file CANNOT import from [browser, react_native] file',
5799
validator.isPlatformCompatible(['node'], ['browser', 'react_native']), false);
58100

59-
console.log('\n4. MULTI-PLATFORM FILES (strictest rules)');
101+
console.log('\n5. MULTI-PLATFORM FILES (strictest rules)');
60102
console.log('-'.repeat(70));
61103
test('[browser, react_native] file CAN import from [browser, react_native] file',
62104
validator.isPlatformCompatible(['browser', 'react_native'], ['browser', 'react_native']), true);
105+
test('[browser, react_native] file CAN import from universal file',
106+
validator.isPlatformCompatible(['browser', 'react_native'], ['__universal__']), true);
63107
test('[browser, react_native] file CANNOT import from browser-only file',
64108
validator.isPlatformCompatible(['browser', 'react_native'], 'browser'), false);
65109
test('[browser, react_native] file CANNOT import from react_native-only file',
66110
validator.isPlatformCompatible(['browser', 'react_native'], 'react_native'), false);
67111
test('[browser, react_native] file CANNOT import from node file',
68112
validator.isPlatformCompatible(['browser', 'react_native'], 'node'), false);
69113

70-
console.log('\n5. SUPPORTED PLATFORMS EXTRACTION');
71-
console.log('-'.repeat(70));
72-
const testExport1 = `export const __platforms = ['browser', 'react_native'];`;
73-
const platforms1 = validator.extractSupportedPlatforms(testExport1);
74-
test('Extract __platforms array',
75-
JSON.stringify(platforms1), JSON.stringify(['browser', 'react_native']));
76-
77-
const testExport2 = `export const __platforms: string[] = ["browser", "node"];`;
78-
const platforms2 = validator.extractSupportedPlatforms(testExport2);
79-
test('Extract __platforms with type annotation',
80-
JSON.stringify(platforms2), JSON.stringify(['browser', 'node']));
81-
82-
const testExport3 = `export const __platforms = ['__universal__'];`;
83-
const platforms3 = validator.extractSupportedPlatforms(testExport3);
84-
test('Extract __universal__ marker',
85-
JSON.stringify(platforms3), JSON.stringify(['__universal__']));
86114

87115
console.log('\n' + '='.repeat(70));
88116
console.log(`\nResults: ${passed} passed, ${failed} failed`);

scripts/validate-platform-isolation-ts.js renamed to scripts/validate-platform-isolation.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
/**
2020
* Platform Isolation Validator
2121
*
22-
* This script ensures that platform-specific entry points only import
23-
* from universal or compatible platform files.
22+
* This script ensures that platform-specific modules only import
23+
* from universal or compatible platform modules.
2424
*
2525
* Platform Detection:
2626
* - ALL source files (except tests) MUST export __platforms array
2727
* - Universal files use: export const __platforms = ['__universal__'];
28-
* - Platform-specific files use: export const __platforms = ['browser', 'node'];
28+
* - Platform-specific files use platforn names, e.g: export const __platforms = ['browser', 'node'];
2929
* - Valid platform values are dynamically read from Platform type in platform_support.ts
3030
*
3131
* Rules:

0 commit comments

Comments
 (0)