Skip to content

Commit 1f19db5

Browse files
Nima21claude
andcommitted
fix: convert build scripts to JavaScript for Node 20 compatibility
- Converted prepare.ts to prepare.js (no longer needs --experimental-strip-types) - Converted post-build.ts to post-build.js - Updated package.json to use .js versions - Fixes CI test failures on Node 20 This ensures the build and prepare scripts work across all Node versions (20-24) without requiring the --experimental-strip-types flag. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 4085397 commit 1f19db5

3 files changed

Lines changed: 185 additions & 2 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"bin": "./build/src/index.js",
77
"main": "index.js",
88
"scripts": {
9-
"build": "tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts",
9+
"build": "tsc && node scripts/post-build.js",
1010
"typecheck": "tsc --noEmit",
1111
"format": "eslint --cache --fix . && prettier --write --cache .",
1212
"check-format": "eslint --cache . && prettier --check --cache .;",
@@ -19,7 +19,7 @@
1919
"test:only": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
2020
"test:only:no-build": "node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
2121
"test:update-snapshots": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-force-exit --test --test-update-snapshots \"build/tests/**/*.test.js\"",
22-
"prepare": "node --experimental-strip-types scripts/prepare.ts",
22+
"prepare": "node scripts/prepare.js",
2323
"sync-server-json-version": "node --experimental-strip-types scripts/sync-server-json-version.ts && npm run format"
2424
},
2525
"files": [

scripts/post-build.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
import * as fs from 'node:fs';
7+
import * as path from 'node:path';
8+
import tsConfig from '../tsconfig.json' with { type: 'json' };
9+
const BUILD_DIR = path.join(process.cwd(), 'build');
10+
/**
11+
* Writes content to a file.
12+
* @param filePath The path to the file.
13+
* @param content The content to write.
14+
*/
15+
function writeFile(filePath, content) {
16+
fs.writeFileSync(filePath, content, 'utf-8');
17+
}
18+
/**
19+
* Replaces content in a file.
20+
* @param filePath The path to the file.
21+
* @param find The regex to find.
22+
* @param replace The string to replace with.
23+
*/
24+
function sed(filePath, find, replace) {
25+
if (!fs.existsSync(filePath)) {
26+
console.warn(`File not found for sed operation: ${filePath}`);
27+
return;
28+
}
29+
const content = fs.readFileSync(filePath, 'utf-8');
30+
const newContent = content.replace(find, replace);
31+
fs.writeFileSync(filePath, newContent, 'utf-8');
32+
}
33+
/**
34+
* Ensures that licenses for third party files we use gets copied into the build/ dir.
35+
*/
36+
function copyThirdPartyLicenseFiles() {
37+
const thirdPartyDirectories = tsConfig.include.filter(location => {
38+
return location.includes('node_modules/chrome-devtools-frontend/front_end/third_party');
39+
});
40+
for (const thirdPartyDir of thirdPartyDirectories) {
41+
const fullPath = path.join(process.cwd(), thirdPartyDir);
42+
const licenseFile = path.join(fullPath, 'LICENSE');
43+
if (!fs.existsSync(licenseFile)) {
44+
console.error('No LICENSE for', path.basename(thirdPartyDir));
45+
}
46+
const destinationDir = path.join(BUILD_DIR, thirdPartyDir);
47+
const destinationFile = path.join(destinationDir, 'LICENSE');
48+
fs.copyFileSync(licenseFile, destinationFile);
49+
}
50+
}
51+
function main() {
52+
const devtoolsThirdPartyPath = 'node_modules/chrome-devtools-frontend/front_end/third_party';
53+
const devtoolsFrontEndCorePath = 'node_modules/chrome-devtools-frontend/front_end/core';
54+
// Create i18n mock
55+
const i18nDir = path.join(BUILD_DIR, devtoolsFrontEndCorePath, 'i18n');
56+
fs.mkdirSync(i18nDir, { recursive: true });
57+
const i18nFile = path.join(i18nDir, 'i18n.js');
58+
const i18nContent = `
59+
export const i18n = {
60+
registerUIStrings: () => {},
61+
getLocalizedString: (_, str) => {
62+
// So that the string passed in gets output verbatim.
63+
return str;
64+
},
65+
lockedLazyString: () => {},
66+
getLazilyComputedLocalizedString: () => {},
67+
};
68+
69+
// TODO(jacktfranklin): once the DocumentLatency insight does not depend on
70+
// this method, we can remove this stub.
71+
export const TimeUtilities = {
72+
millisToString(x) {
73+
const separator = '\xA0';
74+
const formatter = new Intl.NumberFormat('en-US', {
75+
style: 'unit',
76+
unitDisplay: 'narrow',
77+
minimumFractionDigits: 0,
78+
maximumFractionDigits: 1,
79+
unit: 'millisecond',
80+
});
81+
82+
const parts = formatter.formatToParts(x);
83+
for (const part of parts) {
84+
if (part.type === 'literal') {
85+
if (part.value === ' ') {
86+
part.value = separator;
87+
}
88+
}
89+
}
90+
91+
return parts.map(part => part.value).join('');
92+
}
93+
};
94+
95+
// TODO(jacktfranklin): once the ImageDelivery insight does not depend on this method, we can remove this stub.
96+
export const ByteUtilities = {
97+
bytesToString(x) {
98+
const separator = '\xA0';
99+
const formatter = new Intl.NumberFormat('en-US', {
100+
style: 'unit',
101+
unit: 'kilobyte',
102+
unitDisplay: 'narrow',
103+
minimumFractionDigits: 1,
104+
maximumFractionDigits: 1,
105+
});
106+
const parts = formatter.formatToParts(x / 1000);
107+
for (const part of parts) {
108+
if (part.type === 'literal') {
109+
if (part.value === ' ') {
110+
part.value = separator;
111+
}
112+
}
113+
}
114+
115+
return parts.map(part => part.value).join('');
116+
}
117+
};`;
118+
writeFile(i18nFile, i18nContent);
119+
// Create codemirror.next mock.
120+
const codeMirrorDir = path.join(BUILD_DIR, devtoolsThirdPartyPath, 'codemirror.next');
121+
fs.mkdirSync(codeMirrorDir, { recursive: true });
122+
const codeMirrorFile = path.join(codeMirrorDir, 'codemirror.next.js');
123+
const codeMirrorContent = `export default {}`;
124+
writeFile(codeMirrorFile, codeMirrorContent);
125+
// Create root mock
126+
const rootDir = path.join(BUILD_DIR, devtoolsFrontEndCorePath, 'root');
127+
fs.mkdirSync(rootDir, { recursive: true });
128+
const runtimeFile = path.join(rootDir, 'Runtime.js');
129+
const runtimeContent = `
130+
export function getChromeVersion() { return ''; };
131+
export const hostConfig = {};
132+
`;
133+
writeFile(runtimeFile, runtimeContent);
134+
// Update protocol_client to remove:
135+
// 1. self.Protocol assignment
136+
// 2. Call to register backend commands.
137+
const protocolClientDir = path.join(BUILD_DIR, devtoolsFrontEndCorePath, 'protocol_client');
138+
const clientFile = path.join(protocolClientDir, 'protocol_client.js');
139+
const globalAssignment = /self\.Protocol = self\.Protocol \|\| \{\};/;
140+
const registerCommands = /InspectorBackendCommands\.registerCommands\(InspectorBackend\.inspectorBackend\);/;
141+
sed(clientFile, globalAssignment, '');
142+
sed(clientFile, registerCommands, '');
143+
const devtoolsLicensePath = path.join('node_modules', 'chrome-devtools-frontend', 'LICENSE');
144+
const devtoolsLicenseFileSource = path.join(process.cwd(), devtoolsLicensePath);
145+
const devtoolsLicenseFileDestination = path.join(BUILD_DIR, devtoolsLicensePath);
146+
fs.copyFileSync(devtoolsLicenseFileSource, devtoolsLicenseFileDestination);
147+
copyThirdPartyLicenseFiles();
148+
}
149+
main();

scripts/prepare.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {rm} from 'node:fs/promises';
8+
import {resolve} from 'node:path';
9+
10+
const projectRoot = process.cwd();
11+
12+
const filesToRemove = [
13+
'node_modules/chrome-devtools-frontend/package.json',
14+
'node_modules/chrome-devtools-frontend/front_end/models/trace/lantern/testing',
15+
'node_modules/chrome-devtools-frontend/front_end/third_party/intl-messageformat/package/package.json',
16+
'node_modules/chrome-devtools-frontend/front_end/third_party/codemirror.next/codemirror.next.js',
17+
];
18+
19+
async function main() {
20+
console.log('Running prepare script to clean up chrome-devtools-frontend...');
21+
for (const file of filesToRemove) {
22+
const fullPath = resolve(projectRoot, file);
23+
console.log(`Removing: ${file}`);
24+
try {
25+
await rm(fullPath, {recursive: true, force: true});
26+
} catch (error) {
27+
console.error(`Failed to remove ${file}:`, error);
28+
process.exit(1);
29+
}
30+
}
31+
console.log('Clean up of chrome-devtools-frontend complete.');
32+
}
33+
34+
void main();

0 commit comments

Comments
 (0)