@krema-build/krema 0.1.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/krema.js +110 -0
- package/lib/cache.js +18 -0
- package/lib/constants.js +36 -0
- package/lib/download.js +69 -0
- package/lib/jdk.js +73 -0
- package/lib/platform.js +25 -0
- package/lib/resolve.js +143 -0
- package/package.json +42 -0
- package/postinstall.js +50 -0
package/bin/krema.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Krema CLI shim.
|
|
4
|
+
* Resolves the best way to run Krema (native binary, JAR+Java, or JAR+auto-install JDK)
|
|
5
|
+
* and execs the resolved command with inherited stdio.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const { spawn } = require('node:child_process');
|
|
10
|
+
const readline = require('node:readline');
|
|
11
|
+
const { resolve, findJava } = require('../lib/resolve');
|
|
12
|
+
const { read, write } = require('../lib/cache');
|
|
13
|
+
const { installJdk } = require('../lib/jdk');
|
|
14
|
+
const { JAVA_VERSION, VERSION } = require('../lib/constants');
|
|
15
|
+
|
|
16
|
+
async function main() {
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
|
|
19
|
+
// Try cached resolution first (validate that cached files still exist)
|
|
20
|
+
const cached = read();
|
|
21
|
+
if (cached && cached.version === VERSION) {
|
|
22
|
+
if (cached.mode === 'native' && cached.path && fs.existsSync(cached.path)) {
|
|
23
|
+
return exec(cached.path, args);
|
|
24
|
+
}
|
|
25
|
+
if (cached.mode === 'jar' && cached.java && cached.jar && fs.existsSync(cached.jar)) {
|
|
26
|
+
return exec(cached.java, ['--enable-native-access=ALL-UNNAMED', '-jar', cached.jar, ...args]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Full resolution
|
|
31
|
+
const resolved = await resolve();
|
|
32
|
+
|
|
33
|
+
if (resolved.mode === 'native') {
|
|
34
|
+
write({ mode: 'native', path: resolved.binary, version: VERSION });
|
|
35
|
+
return exec(resolved.binary, args);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// JAR mode — need Java
|
|
39
|
+
let javaPath = resolved.java;
|
|
40
|
+
|
|
41
|
+
if (!javaPath) {
|
|
42
|
+
// Interactive prompt for JDK install
|
|
43
|
+
if (process.stdin.isTTY) {
|
|
44
|
+
const answer = await prompt(
|
|
45
|
+
`Java ${JAVA_VERSION} is required but was not found.\n` +
|
|
46
|
+
`Install Eclipse Temurin ${JAVA_VERSION} to ~/.krema/jdk? [Y/n] `
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (answer === '' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
50
|
+
try {
|
|
51
|
+
javaPath = await installJdk();
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error(`\nFailed to install JDK: ${err.message}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
printManualInstallInstructions();
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// Non-interactive: try auto-install
|
|
62
|
+
try {
|
|
63
|
+
javaPath = await installJdk();
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error(`Error: Java ${JAVA_VERSION} is required but was not found.`);
|
|
66
|
+
printManualInstallInstructions();
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
write({ mode: 'jar', java: javaPath, jar: resolved.jar, version: VERSION });
|
|
73
|
+
return exec(javaPath, ['--enable-native-access=ALL-UNNAMED', '-jar', resolved.jar, ...args]);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function exec(command, args) {
|
|
77
|
+
const child = spawn(command, args, { stdio: 'inherit' });
|
|
78
|
+
child.on('error', (err) => {
|
|
79
|
+
console.error(`Failed to start: ${err.message}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
});
|
|
82
|
+
child.on('close', (code) => {
|
|
83
|
+
process.exit(code ?? 1);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function prompt(question) {
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
90
|
+
rl.question(question, (answer) => {
|
|
91
|
+
rl.close();
|
|
92
|
+
resolve(answer.trim());
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function printManualInstallInstructions() {
|
|
98
|
+
console.error('');
|
|
99
|
+
console.error(`To use Krema, install Java ${JAVA_VERSION} and either:`);
|
|
100
|
+
console.error(' - Set KREMA_JAVA_HOME to point at the JDK');
|
|
101
|
+
console.error(` - Ensure /usr/libexec/java_home -v ${JAVA_VERSION} works (macOS)`);
|
|
102
|
+
console.error(' - Or add java to your PATH');
|
|
103
|
+
console.error('');
|
|
104
|
+
console.error('Download from: https://adoptium.net/temurin/releases/');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main().catch((err) => {
|
|
108
|
+
console.error(err.message);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
});
|
package/lib/cache.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { CACHE_FILE, KREMA_HOME } = require('./constants');
|
|
4
|
+
|
|
5
|
+
function read() {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function write(data) {
|
|
14
|
+
fs.mkdirSync(KREMA_HOME, { recursive: true });
|
|
15
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2) + '\n');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = { read, write };
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const os = require('node:os');
|
|
3
|
+
|
|
4
|
+
const VERSION = '0.1.0-rc.10';
|
|
5
|
+
const JAVA_VERSION = '25';
|
|
6
|
+
const GITHUB_REPO = 'krema-build/krema';
|
|
7
|
+
const JAR_NAME = 'krema-cli.jar';
|
|
8
|
+
|
|
9
|
+
const KREMA_HOME = path.join(os.homedir(), '.krema');
|
|
10
|
+
const LIB_DIR = path.join(KREMA_HOME, 'lib');
|
|
11
|
+
const JDK_DIR = path.join(KREMA_HOME, 'jdk');
|
|
12
|
+
const CACHE_FILE = path.join(KREMA_HOME, 'cache.json');
|
|
13
|
+
const TEMURIN_DIR = path.join(JDK_DIR, `temurin-${JAVA_VERSION}`);
|
|
14
|
+
|
|
15
|
+
function jarUrl(version) {
|
|
16
|
+
return `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${JAR_NAME}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function nativeBinaryUrl(version, platformKey) {
|
|
20
|
+
const name = platformKey === 'win32-x64' ? 'krema-windows-x64.exe' : `krema-${platformKey}`;
|
|
21
|
+
return `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${name}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
VERSION,
|
|
26
|
+
JAVA_VERSION,
|
|
27
|
+
GITHUB_REPO,
|
|
28
|
+
JAR_NAME,
|
|
29
|
+
KREMA_HOME,
|
|
30
|
+
LIB_DIR,
|
|
31
|
+
JDK_DIR,
|
|
32
|
+
CACHE_FILE,
|
|
33
|
+
TEMURIN_DIR,
|
|
34
|
+
jarUrl,
|
|
35
|
+
nativeBinaryUrl,
|
|
36
|
+
};
|
package/lib/download.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const https = require('node:https');
|
|
4
|
+
const http = require('node:http');
|
|
5
|
+
|
|
6
|
+
function follow(url) {
|
|
7
|
+
return url.startsWith('https://') ? https : http;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function download(url, dest, { onProgress, quiet } = {}) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
13
|
+
const file = fs.createWriteStream(dest);
|
|
14
|
+
|
|
15
|
+
const get = (currentUrl) => {
|
|
16
|
+
follow(currentUrl).get(currentUrl, (res) => {
|
|
17
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
18
|
+
res.resume();
|
|
19
|
+
get(res.headers.location);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (res.statusCode !== 200) {
|
|
24
|
+
file.close();
|
|
25
|
+
fs.unlinkSync(dest);
|
|
26
|
+
reject(new Error(`Download failed: HTTP ${res.statusCode} for ${currentUrl}`));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const total = parseInt(res.headers['content-length'], 10) || 0;
|
|
31
|
+
let downloaded = 0;
|
|
32
|
+
|
|
33
|
+
res.on('data', (chunk) => {
|
|
34
|
+
downloaded += chunk.length;
|
|
35
|
+
if (onProgress && total > 0) {
|
|
36
|
+
onProgress(downloaded, total);
|
|
37
|
+
} else if (!quiet && total > 0 && process.stderr.isTTY) {
|
|
38
|
+
const pct = Math.round((downloaded / total) * 100);
|
|
39
|
+
const mb = (downloaded / 1048576).toFixed(1);
|
|
40
|
+
const totalMb = (total / 1048576).toFixed(1);
|
|
41
|
+
process.stderr.write(`\r Downloading... ${mb}/${totalMb} MB (${pct}%)`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
res.pipe(file);
|
|
46
|
+
|
|
47
|
+
file.on('finish', () => {
|
|
48
|
+
if (!quiet && total > 0 && process.stderr.isTTY) {
|
|
49
|
+
process.stderr.write('\n');
|
|
50
|
+
}
|
|
51
|
+
file.close(resolve);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
file.on('error', (err) => {
|
|
55
|
+
fs.unlinkSync(dest);
|
|
56
|
+
reject(err);
|
|
57
|
+
});
|
|
58
|
+
}).on('error', (err) => {
|
|
59
|
+
file.close();
|
|
60
|
+
try { fs.unlinkSync(dest); } catch {}
|
|
61
|
+
reject(err);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
get(url);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { download };
|
package/lib/jdk.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { execFileSync } = require('node:child_process');
|
|
4
|
+
const { getPlatform } = require('./platform');
|
|
5
|
+
const { JAVA_VERSION, JDK_DIR, TEMURIN_DIR } = require('./constants');
|
|
6
|
+
const { download } = require('./download');
|
|
7
|
+
|
|
8
|
+
function adoptiumUrl() {
|
|
9
|
+
const platform = getPlatform();
|
|
10
|
+
if (!platform) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return `https://api.adoptium.net/v3/binary/latest/${JAVA_VERSION}/ga/${platform.adoptiumOs}/${platform.adoptiumArch}/jdk/hotspot/normal/eclipse`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function temurinJavaPath() {
|
|
17
|
+
// After extraction, Temurin tarballs contain a top-level directory like
|
|
18
|
+
// jdk-25+36 (or jdk-25.0.1+9 etc). We find it dynamically.
|
|
19
|
+
if (!fs.existsSync(TEMURIN_DIR)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const entries = fs.readdirSync(TEMURIN_DIR);
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
const candidate = process.platform === 'darwin'
|
|
25
|
+
? path.join(TEMURIN_DIR, entry, 'Contents', 'Home', 'bin', 'java')
|
|
26
|
+
: path.join(TEMURIN_DIR, entry, 'bin', 'java');
|
|
27
|
+
if (fs.existsSync(candidate)) {
|
|
28
|
+
return candidate;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function installJdk() {
|
|
35
|
+
const url = adoptiumUrl();
|
|
36
|
+
if (!url) {
|
|
37
|
+
throw new Error(`No Adoptium JDK available for ${process.platform}-${process.arch}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ext = process.platform === 'win32' ? '.zip' : '.tar.gz';
|
|
41
|
+
const tmpFile = path.join(JDK_DIR, `temurin-${JAVA_VERSION}${ext}`);
|
|
42
|
+
|
|
43
|
+
console.error(` Downloading Eclipse Temurin ${JAVA_VERSION}...`);
|
|
44
|
+
await download(url, tmpFile);
|
|
45
|
+
|
|
46
|
+
console.error(' Extracting...');
|
|
47
|
+
fs.mkdirSync(TEMURIN_DIR, { recursive: true });
|
|
48
|
+
|
|
49
|
+
if (ext === '.tar.gz') {
|
|
50
|
+
execFileSync('tar', ['xzf', tmpFile, '-C', TEMURIN_DIR], { stdio: 'pipe' });
|
|
51
|
+
} else {
|
|
52
|
+
// Windows: use PowerShell to extract zip
|
|
53
|
+
execFileSync('powershell', [
|
|
54
|
+
'-NoProfile', '-Command',
|
|
55
|
+
`Expand-Archive -Path '${tmpFile}' -DestinationPath '${TEMURIN_DIR}' -Force`
|
|
56
|
+
], { stdio: 'pipe' });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fs.unlinkSync(tmpFile);
|
|
60
|
+
|
|
61
|
+
const javaPath = temurinJavaPath();
|
|
62
|
+
if (!javaPath) {
|
|
63
|
+
throw new Error('JDK extraction succeeded but java binary not found');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Verify
|
|
67
|
+
const out = execFileSync(javaPath, ['-version'], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
68
|
+
// java -version writes to stderr
|
|
69
|
+
console.error(` Installed: ${javaPath}`);
|
|
70
|
+
return javaPath;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = { adoptiumUrl, temurinJavaPath, installJdk };
|
package/lib/platform.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const os = require('node:os');
|
|
2
|
+
const process = require('node:process');
|
|
3
|
+
|
|
4
|
+
const PLATFORMS = {
|
|
5
|
+
'darwin-arm64': { npmPkg: '@krema-build/cli-darwin-arm64', adoptiumOs: 'mac', adoptiumArch: 'aarch64' },
|
|
6
|
+
'darwin-x64': { npmPkg: '@krema-build/cli-darwin-x64', adoptiumOs: 'mac', adoptiumArch: 'x64' },
|
|
7
|
+
'linux-x64': { npmPkg: '@krema-build/cli-linux-x64', adoptiumOs: 'linux', adoptiumArch: 'x64' },
|
|
8
|
+
'linux-arm64': { npmPkg: '@krema-build/cli-linux-arm64', adoptiumOs: 'linux', adoptiumArch: 'aarch64' },
|
|
9
|
+
'win32-x64': { npmPkg: '@krema-build/cli-win32-x64', adoptiumOs: 'windows', adoptiumArch: 'x64' },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function getPlatformKey() {
|
|
13
|
+
return `${process.platform}-${os.arch()}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getPlatform() {
|
|
17
|
+
const key = getPlatformKey();
|
|
18
|
+
const platform = PLATFORMS[key];
|
|
19
|
+
if (!platform) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return { key, ...platform };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { PLATFORMS, getPlatformKey, getPlatform };
|
package/lib/resolve.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { execFileSync } = require('node:child_process');
|
|
4
|
+
const { getPlatform } = require('./platform');
|
|
5
|
+
const { JAVA_VERSION, LIB_DIR, JAR_NAME, VERSION } = require('./constants');
|
|
6
|
+
const { jarUrl } = require('./constants');
|
|
7
|
+
const { download } = require('./download');
|
|
8
|
+
const { temurinJavaPath } = require('./jdk');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Try to find the native binary from the platform-specific npm optional dependency.
|
|
12
|
+
* Returns the path to the binary, or null.
|
|
13
|
+
*/
|
|
14
|
+
function findNativeBinary() {
|
|
15
|
+
const platform = getPlatform();
|
|
16
|
+
if (!platform) return null;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const pkgDir = path.dirname(require.resolve(`${platform.npmPkg}/package.json`));
|
|
20
|
+
const bin = path.join(pkgDir, 'bin', process.platform === 'win32' ? 'krema.exe' : 'krema');
|
|
21
|
+
if (fs.existsSync(bin)) {
|
|
22
|
+
return bin;
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// Package not installed (wrong platform or optional dep skipped)
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Find the fat JAR. Downloads it if not present.
|
|
32
|
+
* Returns the path to the JAR.
|
|
33
|
+
*/
|
|
34
|
+
async function findOrDownloadJar() {
|
|
35
|
+
const jarPath = path.join(LIB_DIR, JAR_NAME);
|
|
36
|
+
if (fs.existsSync(jarPath)) {
|
|
37
|
+
return jarPath;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.error(` Downloading ${JAR_NAME}...`);
|
|
41
|
+
await download(jarUrl(VERSION), jarPath);
|
|
42
|
+
return jarPath;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Find a Java 25 installation.
|
|
47
|
+
* Search order:
|
|
48
|
+
* 1. KREMA_JAVA_HOME env
|
|
49
|
+
* 2. macOS: /usr/libexec/java_home -v 25
|
|
50
|
+
* 3. JAVA_HOME (if version matches)
|
|
51
|
+
* 4. java on PATH (if version matches)
|
|
52
|
+
* 5. ~/.krema/jdk/temurin-25/
|
|
53
|
+
* Returns the path to the java binary, or null.
|
|
54
|
+
*/
|
|
55
|
+
function findJava() {
|
|
56
|
+
// 1. KREMA_JAVA_HOME override
|
|
57
|
+
if (process.env.KREMA_JAVA_HOME) {
|
|
58
|
+
const java = path.join(process.env.KREMA_JAVA_HOME, 'bin', 'java');
|
|
59
|
+
if (fs.existsSync(java)) return java;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. macOS: /usr/libexec/java_home
|
|
63
|
+
if (process.platform === 'darwin') {
|
|
64
|
+
try {
|
|
65
|
+
const jh = execFileSync('/usr/libexec/java_home', ['-v', JAVA_VERSION], {
|
|
66
|
+
encoding: 'utf8',
|
|
67
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
68
|
+
}).trim();
|
|
69
|
+
const java = path.join(jh, 'bin', 'java');
|
|
70
|
+
if (jh && fs.existsSync(java)) return java;
|
|
71
|
+
} catch {
|
|
72
|
+
// Not found
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 3. JAVA_HOME if version matches
|
|
77
|
+
if (process.env.JAVA_HOME) {
|
|
78
|
+
const java = path.join(process.env.JAVA_HOME, 'bin', 'java');
|
|
79
|
+
if (fs.existsSync(java) && checkJavaVersion(java)) return java;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 4. java on PATH
|
|
83
|
+
try {
|
|
84
|
+
const javaOnPath = execFileSync(process.platform === 'win32' ? 'where' : 'which', ['java'], {
|
|
85
|
+
encoding: 'utf8',
|
|
86
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
87
|
+
}).trim().split('\n')[0];
|
|
88
|
+
if (javaOnPath && checkJavaVersion(javaOnPath)) return javaOnPath;
|
|
89
|
+
} catch {
|
|
90
|
+
// Not found
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 5. ~/.krema/jdk/temurin-25/
|
|
94
|
+
const temurin = temurinJavaPath();
|
|
95
|
+
if (temurin) return temurin;
|
|
96
|
+
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function checkJavaVersion(javaPath) {
|
|
101
|
+
try {
|
|
102
|
+
const output = execFileSync(javaPath, ['-version'], {
|
|
103
|
+
encoding: 'utf8',
|
|
104
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
105
|
+
});
|
|
106
|
+
// java -version outputs to stderr, but execFileSync with stdio pipe captures it
|
|
107
|
+
// Some JDKs output to stdout, some to stderr. Check both.
|
|
108
|
+
const combined = output || '';
|
|
109
|
+
return parseJavaVersion(combined) === JAVA_VERSION;
|
|
110
|
+
} catch (err) {
|
|
111
|
+
// java -version writes to stderr which becomes err.stderr
|
|
112
|
+
if (err.stderr) {
|
|
113
|
+
return parseJavaVersion(err.stderr) === JAVA_VERSION;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parseJavaVersion(text) {
|
|
120
|
+
const match = text.match(/"(\d+)/);
|
|
121
|
+
return match ? match[1] : null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Resolve the execution mode. Returns { mode, args } where:
|
|
126
|
+
* - mode "native": args = [binaryPath, ...userArgs]
|
|
127
|
+
* - mode "jar": args = [javaPath, "-jar", jarPath, ...userArgs]
|
|
128
|
+
*/
|
|
129
|
+
async function resolve() {
|
|
130
|
+
// Tier 1: Native binary
|
|
131
|
+
const nativeBin = findNativeBinary();
|
|
132
|
+
if (nativeBin) {
|
|
133
|
+
return { mode: 'native', binary: nativeBin, java: null, jar: null };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Tier 2 & 3: JAR + Java
|
|
137
|
+
const jarPath = await findOrDownloadJar();
|
|
138
|
+
const javaPath = findJava();
|
|
139
|
+
|
|
140
|
+
return { mode: 'jar', binary: null, java: javaPath, jar: jarPath };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { findNativeBinary, findOrDownloadJar, findJava, resolve };
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@krema-build/krema",
|
|
3
|
+
"version": "0.1.0-rc.10",
|
|
4
|
+
"description": "Lightweight desktop apps with system webviews — CLI tool",
|
|
5
|
+
"bin": {
|
|
6
|
+
"krema": "bin/krema.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node postinstall.js"
|
|
10
|
+
},
|
|
11
|
+
"optionalDependencies": {
|
|
12
|
+
"@krema-build/cli-darwin-arm64": "0.1.0-rc.10",
|
|
13
|
+
"@krema-build/cli-linux-x64": "0.1.0-rc.10",
|
|
14
|
+
"@krema-build/cli-linux-arm64": "0.1.0-rc.10",
|
|
15
|
+
"@krema-build/cli-win32-x64": "0.1.0-rc.10"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin/",
|
|
19
|
+
"lib/",
|
|
20
|
+
"postinstall.js"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"krema",
|
|
27
|
+
"desktop",
|
|
28
|
+
"webview",
|
|
29
|
+
"gui",
|
|
30
|
+
"native",
|
|
31
|
+
"cli"
|
|
32
|
+
],
|
|
33
|
+
"license": "BSL-1.1",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/krema-build/krema.git",
|
|
37
|
+
"directory": "krema-npm/packages/krema"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Postinstall script for the krema npm package.
|
|
4
|
+
* Non-interactive: detects the best execution mode and caches the result.
|
|
5
|
+
* Always exits 0 to never break npm install.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { findNativeBinary, findJava } = require('./lib/resolve');
|
|
9
|
+
const { download } = require('./lib/download');
|
|
10
|
+
const { read, write } = require('./lib/cache');
|
|
11
|
+
const { VERSION, LIB_DIR, JAR_NAME, jarUrl } = require('./lib/constants');
|
|
12
|
+
const path = require('node:path');
|
|
13
|
+
const fs = require('node:fs');
|
|
14
|
+
|
|
15
|
+
async function main() {
|
|
16
|
+
// 1. Check for native binary
|
|
17
|
+
const nativeBin = findNativeBinary();
|
|
18
|
+
if (nativeBin) {
|
|
19
|
+
write({ mode: 'native', path: nativeBin, version: VERSION });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 2. No native binary — download JAR
|
|
24
|
+
const jarPath = path.join(LIB_DIR, JAR_NAME);
|
|
25
|
+
if (!fs.existsSync(jarPath)) {
|
|
26
|
+
try {
|
|
27
|
+
await download(jarUrl(VERSION), jarPath, { quiet: true });
|
|
28
|
+
} catch {
|
|
29
|
+
// Non-fatal: bin/krema.js will retry at runtime
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Only cache if the JAR was actually downloaded
|
|
34
|
+
if (!fs.existsSync(jarPath)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 3. Check for Java 25
|
|
39
|
+
const javaPath = findJava();
|
|
40
|
+
if (javaPath) {
|
|
41
|
+
write({ mode: 'jar', java: javaPath, jar: jarPath, version: VERSION });
|
|
42
|
+
} else {
|
|
43
|
+
write({ mode: 'jar-no-java', jar: jarPath, version: VERSION });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
main().catch(() => {
|
|
48
|
+
// Never break npm install
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|