Skip to content

Commit e9842cf

Browse files
cortinicometa-codesync[bot]
authored andcommitted
Add Kotlin to yarn format
Summary: Add `yarn format-kotlin` and `yarn format-check-kotlin` using the npm `ktfmt` package and its bundled formatter jar. This adds the offline package mirror and workspace lock entry without changing existing Kotlin source formatting or Gradle configuration. The wrapper discovers a suitable JDK when available and otherwise prints environment-specific Java 17 setup guidance before skipping Kotlin. allow-large-files: The npm package intentionally contains the upstream ktfmt executable jar so offline and public installs use the same formatter. Changelog: [Internal] Differential Revision: D119487612
1 parent b4bc64c commit e9842cf

4 files changed

Lines changed: 206 additions & 4 deletions

File tree

‎package.json‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
"cxx-api-validate": "python -m scripts.cxx-api.parser --validate",
1515
"flow-check": "flow full-check",
1616
"flow": "flow",
17-
"format-check": "yarn format-check-javascript && yarn format-check-cpp",
17+
"format-check": "yarn format-check-javascript && yarn format-check-cpp && yarn format-check-kotlin",
1818
"format-check-cpp": "node ./scripts/clang-format.js --check",
1919
"format-check-javascript": "prettier --check \"./**/*.{cjs,cts,flow,js,jsx,md,mjs,mts,ts,tsx,yaml,yml}\"",
20-
"format": "yarn format-javascript && yarn format-cpp",
20+
"format-check-kotlin": "node ./scripts/format-kotlin.js --check",
21+
"format": "yarn format-javascript && yarn format-cpp && yarn format-kotlin",
2122
"format-cpp": "node ./scripts/clang-format.js",
2223
"format-javascript": "prettier --write \"./**/*.{cjs,cts,flow,js,jsx,md,mjs,mts,ts,tsx,yaml,yml}\"",
24+
"format-kotlin": "node ./scripts/format-kotlin.js",
2325
"featureflags": "yarn --cwd packages/react-native featureflags",
2426
"js-api-diff": "node ./scripts/js-api/diff-api-snapshot",
25-
"lint-kotlin-check": "./gradlew ktfmtCheck",
26-
"lint-kotlin": "./gradlew ktfmtFormat",
2727
"lint-markdown": "markdownlint-cli2 2>&1",
2828
"lint": "eslint --max-warnings 0 .",
2929
"preinstall": "node ./scripts/try-set-hermes-compiler-prebuilt.js",
@@ -101,6 +101,7 @@
101101
"jest-junit": "^16.0.0",
102102
"jest-snapshot": "^29.7.0",
103103
"jsonc-parser": "2.2.1",
104+
"ktfmt": "0.59.0",
104105
"markdownlint-cli2": "^0.17.2",
105106
"markdownlint-rule-relative-links": "^3.0.0",
106107
"memfs": "^4.38.2",

‎scripts/format-kotlin.js‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @noflow
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
const {findJava, warnMissingJava} = require('./format-utils');
14+
const {spawnSync} = require('node:child_process');
15+
const fs = require('node:fs');
16+
const path = require('node:path');
17+
const {globSync} = require('tinyglobby');
18+
19+
const REPO_ROOT = path.resolve(__dirname, '..');
20+
const KTFMT_JAR = require.resolve('ktfmt/lib/ktfmt.jar');
21+
const GENERATED_MARKER = Buffer.from('@' + 'generated');
22+
const MINIMUM_JAVA_VERSION = 17;
23+
const MAX_FILES_PER_PROCESS = 100;
24+
const MAX_HEADER_BYTES = 4096;
25+
const IGNORE = [
26+
'**/build/**',
27+
'**/com/facebook/yoga/**',
28+
'**/hermes-engine/**',
29+
'**/internal/featureflags/**',
30+
'**/node_modules/**',
31+
'**/systeminfo/ReactNativeVersion.kt',
32+
];
33+
34+
function isGenerated(file) {
35+
const fd = fs.openSync(path.resolve(REPO_ROOT, file), 'r');
36+
try {
37+
const header = Buffer.alloc(MAX_HEADER_BYTES);
38+
const bytesRead = fs.readSync(fd, header, 0, header.length, 0);
39+
return header.subarray(0, bytesRead).includes(GENERATED_MARKER);
40+
} finally {
41+
fs.closeSync(fd);
42+
}
43+
}
44+
45+
function main() {
46+
const check = process.argv[2] === '--check';
47+
const java = findJava(MINIMUM_JAVA_VERSION);
48+
if (java == null) {
49+
warnMissingJava('Kotlin');
50+
return;
51+
}
52+
const files = globSync('**/*.{kt,kts}', {
53+
cwd: REPO_ROOT,
54+
ignore: IGNORE,
55+
}).filter(file => !isGenerated(file));
56+
for (let i = 0; i < files.length; i += MAX_FILES_PER_PROCESS) {
57+
const result = spawnSync(
58+
java,
59+
[
60+
'-jar',
61+
KTFMT_JAR,
62+
'--do-not-remove-unused-imports',
63+
...(check ? ['--dry-run', '--set-exit-if-changed'] : []),
64+
...files.slice(i, i + MAX_FILES_PER_PROCESS),
65+
],
66+
{cwd: REPO_ROOT, stdio: 'inherit'},
67+
);
68+
if (result.error != null) {
69+
throw result.error;
70+
}
71+
if (result.signal != null) {
72+
process.kill(process.pid, result.signal);
73+
return;
74+
}
75+
if (result.status !== 0) {
76+
process.exit(result.status ?? 1);
77+
}
78+
}
79+
}
80+
81+
main();

‎scripts/format-utils.js‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @noflow
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
const {spawnSync} = require('node:child_process');
14+
const fs = require('node:fs');
15+
const path = require('node:path');
16+
17+
const REPO_ROOT = path.resolve(__dirname, '..');
18+
const FBSOURCE_ROOT = path.resolve(REPO_ROOT, '../../..');
19+
const IS_META_CHECKOUT =
20+
fs.existsSync(path.join(FBSOURCE_ROOT, 'tools', 'arcanist')) &&
21+
fs.existsSync(path.join(FBSOURCE_ROOT, 'xplat', 'js'));
22+
23+
function commandVersion(command, prefixArguments = []) {
24+
const result = spawnSync(command, [...prefixArguments, '--version'], {
25+
encoding: 'utf8',
26+
});
27+
return {
28+
output: `${result.stdout ?? ''}\n${result.stderr ?? ''}`,
29+
status: result.status,
30+
};
31+
}
32+
33+
function findSystemDotslash() {
34+
for (const candidate of ['/usr/bin/dotslash', '/usr/local/bin/dotslash']) {
35+
if (fs.existsSync(candidate)) {
36+
return candidate;
37+
}
38+
}
39+
return null;
40+
}
41+
42+
function findMetaTool(...relativePath) {
43+
if (!IS_META_CHECKOUT) {
44+
return null;
45+
}
46+
const dotslash = findSystemDotslash();
47+
const manifest = path.join(FBSOURCE_ROOT, ...relativePath);
48+
if (dotslash == null || !fs.existsSync(manifest)) {
49+
return null;
50+
}
51+
return {command: dotslash, prefixArguments: [manifest]};
52+
}
53+
54+
function javaMajorVersion(command) {
55+
const result = spawnSync(command, ['-version'], {encoding: 'utf8'});
56+
const output = `${result.stdout ?? ''}\n${result.stderr ?? ''}`;
57+
const version = /version "(?:1\.)?(\d+)/.exec(output);
58+
return result.status === 0 && version != null ? Number(version[1]) : null;
59+
}
60+
61+
function findJava(minimumVersion) {
62+
if (process.env.JAVA != null) {
63+
return javaMajorVersion(process.env.JAVA) >= minimumVersion
64+
? process.env.JAVA
65+
: null;
66+
}
67+
68+
const candidates = [];
69+
if (IS_META_CHECKOUT) {
70+
if (process.platform === 'darwin') {
71+
for (const version of [17, 21]) {
72+
const result = spawnSync(
73+
'/usr/libexec/java_home',
74+
['-v', String(version)],
75+
{
76+
encoding: 'utf8',
77+
},
78+
);
79+
if (result.status === 0 && result.stdout.trim() !== '') {
80+
candidates.push(path.join(result.stdout.trim(), 'bin', 'java'));
81+
}
82+
}
83+
} else {
84+
candidates.push(
85+
'/usr/local/java-runtime/impl/17/bin/java',
86+
'/usr/local/java-runtime/17/bin/java',
87+
'/usr/local/java-runtime/impl/21/bin/java',
88+
'/usr/local/java-runtime/21/bin/java',
89+
);
90+
}
91+
}
92+
candidates.push('java');
93+
94+
return (
95+
candidates.find(command => javaMajorVersion(command) >= minimumVersion) ??
96+
null
97+
);
98+
}
99+
100+
function warnMissingJava(language) {
101+
const instructions = IS_META_CHECKOUT
102+
? 'Meta: repair the managed `java-runtime` installation, or set JAVA=/path/to/java. Expected Java at `/usr/local/java-runtime/impl/17/bin/java` on Linux or from `/usr/libexec/java_home -v 17` on macOS.'
103+
: 'Please install a JDK of your choice with Java 17 or newer and make sure the `java` command is in your PATH, or set JAVA=/path/to/java.';
104+
console.warn(
105+
`warning: Skipping ${language} formatting because Java 17 or newer was not found.\n${instructions}`,
106+
);
107+
}
108+
109+
module.exports = {
110+
commandVersion,
111+
findJava,
112+
findMetaTool,
113+
IS_META_CHECKOUT,
114+
warnMissingJava,
115+
};

‎yarn.lock‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6373,6 +6373,11 @@ kleur@^3.0.3:
63736373
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
63746374
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
63756375

6376+
ktfmt@0.59.0:
6377+
version "0.59.0"
6378+
resolved "https://registry.yarnpkg.com/ktfmt/-/ktfmt-0.59.0.tgz#99f98b81dbdc7f1487dfbc9850eb17b3780cf6d5"
6379+
integrity sha512-lOEn/7y2Ez2/nxDTn5EwJv6BSugB8BtzY2Gn6GvyLIAjdUf3xgKzirIxD57t/vu5I6eybivmVtONI3WGXyZ3lw==
6380+
63766381
language-subtag-registry@^0.3.20:
63776382
version "0.3.23"
63786383
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7"

0 commit comments

Comments
 (0)