Skip to content

Commit ff2f720

Browse files
committed
fix: validator scripts
1 parent cd06509 commit ff2f720

5 files changed

Lines changed: 79 additions & 38 deletions

File tree

‎.github/workflows/Build.yml‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,3 @@ jobs:
7676
run: pnpm run validate-manifest-format
7777
- name: "Validating native dependencies"
7878
run: pnpm run validate-native-dependencies
79-
env:
80-
CI_COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.pull_request.title }}

‎configs/jsactions/rollup-plugin-collect-dependencies.mjs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ async function hasNativeCode(dir) {
131131
["**/{android,ios}/*", "**/*.podspec"],
132132
{
133133
cwd: dir,
134-
ignore: ["**/example*/**", "**/__tests__/**", "**/docs/**"]
134+
ignore: ["**/example*/**", "**/__tests__/**", "**/docs/**", "**/.github/**"],
135+
caseSensitiveMatch: false
135136
}
136137
)).length > 0;
137138
}
@@ -193,7 +194,7 @@ export async function copyJsModule(moduleSourcePath, to) {
193194

194195
// Skip certain directories
195196
if (
196-
relativePath.match(/(^|[\\/])(android|ios|windows|mac|jest|github|gradle|__.*__|docs|example.*)([\\/]|$)/)
197+
relativePath.match(/(^|[\\/])(android|ios|windows|mac|jest|\.github|gradle|__.*__|docs|example.*)([\\/]|$)/i)
197198
) {
198199
return false;
199200
}

‎package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
"build-mpk": "ts-node --project ./scripts/tsconfig.json ./scripts/release/build-mpk.ts",
3131
"version": "ts-node --project ./scripts/tsconfig.json ./scripts/release/BumpVersion.ts",
3232
"validate-staged-widget-versions": "node scripts/validation/validate-versions-staged-files.js",
33-
"validate-manifest-format": "node scripts/validation/validate-manifest-format.js",
34-
"validate-native-dependencies": "node scripts/validation/validate-native-dependencies.js",
33+
"validate-manifest-format": "node --experimental-strip-types scripts/validation/validate-manifest-format.ts",
34+
"validate-native-dependencies": "node --experimental-strip-types scripts/validation/validate-native-dependencies.ts",
3535
"setup-mobile": "pnpm setup-android && pnpm setup-ios",
3636
"build:widgets": "node ./scripts/widget/buildWidgets.js",
3737
"test_widgets:maestro:ios": "bash maestro/run_maestro_widget_tests.sh ios",

scripts/validation/validate-manifest-format.js renamed to scripts/validation/validate-manifest-format.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const { readFileSync, existsSync, readdirSync } = require("fs");
2-
const { join } = require("path");
1+
import { readFileSync, existsSync, readdirSync } from "fs";
2+
import { join } from "path";
33

44
/**
55
* CI Check: Prevent Empty Manifest Objects
@@ -13,15 +13,26 @@ const { join } = require("path");
1313
* - Widgets WITHOUT native dependencies: emit NO FILE at all
1414
*/
1515

16+
interface Manifest {
17+
nativeDependencies?: Record<string, string>;
18+
[key: string]: unknown;
19+
}
20+
21+
interface Violation {
22+
widget: string;
23+
file: string;
24+
reason: string;
25+
}
26+
1627
try {
1728
validateManifestFormat();
1829
} catch (error) {
1930
console.error(error);
2031
process.exit(1);
2132
}
2233

23-
function validateManifestFormat() {
24-
const violations = [];
34+
function validateManifestFormat(): void {
35+
const violations: Violation[] = [];
2536
const widgetsDir = join(process.cwd(), "packages/pluggableWidgets");
2637

2738
if (!existsSync(widgetsDir)) {
@@ -56,7 +67,7 @@ function validateManifestFormat() {
5667
continue;
5768
}
5869

59-
const manifest = JSON.parse(content);
70+
const manifest: Manifest = JSON.parse(content);
6071

6172
// Check for empty manifest or empty nativeDependencies object
6273
const isEmpty =
@@ -78,7 +89,7 @@ function validateManifestFormat() {
7889
violations.push({
7990
widget,
8091
file,
81-
reason: `Invalid JSON: ${error.message}`
92+
reason: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`
8293
});
8394
}
8495
}

scripts/validation/validate-native-dependencies.js renamed to scripts/validation/validate-native-dependencies.ts

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const { existsSync, readdirSync, readFileSync } = require("fs");
2-
const { join } = require("path");
3-
const { execSync } = require("child_process");
4-
const fg = require("fast-glob");
1+
import { existsSync, readdirSync, readFileSync } from "fs";
2+
import { join } from "path";
3+
import { execSync } from "child_process";
4+
import * as fg from "fast-glob";
55

66
/**
77
* CI Check: Detect New Native Dependencies
@@ -11,39 +11,68 @@ const fg = require("fast-glob");
1111
* which forces customers to rebuild their mobile apps.
1212
*
1313
* How it works:
14-
* - Compares package.json dependencies against previous commit (HEAD)
14+
* - Compares package.json dependencies against parent commit (HEAD^)
1515
* - For any NEW dependencies, checks if they contain native code
1616
* - Warns if the new dependency has ios/android folders
1717
*
1818
* Bypass: Include "NATIVE_DEPENDENCY_APPROVED" in the commit message.
1919
*/
2020

21+
interface PackageJson {
22+
dependencies?: Record<string, string>;
23+
peerDependencies?: Record<string, string>;
24+
}
25+
26+
interface Violation {
27+
widget: string;
28+
dependency: string;
29+
path: string;
30+
}
31+
2132
try {
22-
validateNativeDependencies();
33+
await validateNativeDependencies();
2334
} catch (error) {
2435
console.error(error);
2536
process.exit(1);
2637
}
2738

28-
async function validateNativeDependencies() {
39+
async function validateNativeDependencies(): Promise<void> {
2940
// Check for bypass approval
30-
// In CI: read from environment variable
31-
// In commit-msg hook: read from file path passed as argument
32-
let commitMessage = process.env.CI_COMMIT_MESSAGE || "";
41+
let isApproved = false;
3342

34-
const commitMsgFile = process.argv[2]; // Path from commit-msg hook
43+
// In commit-msg hook: read from file path passed as argument
44+
const commitMsgFile = process.argv[2];
3545
if (commitMsgFile && existsSync(commitMsgFile)) {
36-
commitMessage = readFileSync(commitMsgFile, "utf-8");
46+
const commitMessage = readFileSync(commitMsgFile, "utf-8");
47+
isApproved = commitMessage.includes("NATIVE_DEPENDENCY_APPROVED");
48+
} else {
49+
// In CI: check all commits between HEAD^ and HEAD
50+
try {
51+
const commitMessages = execSync("git log HEAD^..HEAD --format=%B", {
52+
encoding: "utf-8",
53+
stdio: ["pipe", "pipe", "ignore"]
54+
});
55+
isApproved = commitMessages.includes("NATIVE_DEPENDENCY_APPROVED");
56+
} catch (error) {
57+
// If git log fails (e.g., no parent commit), fall back to checking HEAD only
58+
try {
59+
const commitMessage = execSync("git log -1 --format=%B", {
60+
encoding: "utf-8",
61+
stdio: ["pipe", "pipe", "ignore"]
62+
});
63+
isApproved = commitMessage.includes("NATIVE_DEPENDENCY_APPROVED");
64+
} catch (fallbackError) {
65+
// If even that fails, continue with validation
66+
}
67+
}
3768
}
3869

39-
const isApproved = commitMessage.includes("NATIVE_DEPENDENCY_APPROVED");
40-
4170
if (isApproved) {
4271
console.log("✅ Native dependency changes approved via commit message");
4372
return;
4473
}
4574

46-
const violations = [];
75+
const violations: Violation[] = [];
4776
const widgetsDir = join(process.cwd(), "packages/pluggableWidgets");
4877

4978
if (!existsSync(widgetsDir)) {
@@ -64,21 +93,22 @@ async function validateNativeDependencies() {
6493

6594
try {
6695
// Get current package.json
67-
const currentPackageJson = require(packageJsonPath);
68-
const currentDeps = {
96+
const currentPackageJson: PackageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
97+
const currentDeps: Record<string, string> = {
6998
...currentPackageJson.dependencies,
7099
...currentPackageJson.peerDependencies
71100
};
72101

73-
// Get previous package.json from git (HEAD)
74-
let previousDeps = {};
102+
// Get previous package.json from git (HEAD^)
103+
let previousDeps: Record<string, string> = {};
75104
try {
76-
const relativePath = join("packages/pluggableWidgets", widget, "package.json");
77-
const previousContent = execSync(`git show HEAD:${relativePath}`, {
105+
// Git always uses forward slashes, even on Windows
106+
const relativePath = join("packages/pluggableWidgets", widget, "package.json").replace(/\\/g, "/");
107+
const previousContent = execSync(`git show HEAD^:${relativePath}`, {
78108
encoding: "utf-8",
79109
stdio: ["pipe", "pipe", "ignore"]
80110
});
81-
const previousPackageJson = JSON.parse(previousContent);
111+
const previousPackageJson: PackageJson = JSON.parse(previousContent);
82112
previousDeps = {
83113
...previousPackageJson.dependencies,
84114
...previousPackageJson.peerDependencies
@@ -110,7 +140,7 @@ async function validateNativeDependencies() {
110140
}
111141
}
112142
} catch (error) {
113-
console.warn(`⚠️ Could not check ${widget}: ${error.message}`);
143+
console.warn(`⚠️ Could not check ${widget}: ${error instanceof Error ? error.message : String(error)}`);
114144
}
115145
}
116146

@@ -140,11 +170,12 @@ async function validateNativeDependencies() {
140170
* Check if a dependency contains native code
141171
* Same logic as the patched hasNativeCode function
142172
*/
143-
async function hasNativeCode(dir) {
173+
async function hasNativeCode(dir: string): Promise<boolean> {
144174
try {
145-
const files = await fg(["**/{android,ios}/*", "**/*.podspec"], {
175+
const files = await fg.default(["**/{android,ios}/*", "**/*.podspec"], {
146176
cwd: dir,
147-
ignore: ["**/example*/**", "**/__tests__/**", "**/docs/**"]
177+
ignore: ["**/example*/**", "**/__tests__/**", "**/docs/**", "**/.github/**"],
178+
caseSensitiveMatch: false
148179
});
149180
return files.length > 0;
150181
} catch (error) {

0 commit comments

Comments
 (0)