@simplelocalize/cli 2.9.3 → 3.0.0
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/README.md +24 -11
- package/index.cjs +115 -120
- package/package.json +9 -2
- package/.github/workflows/ci-publish.yml +0 -35
package/README.md
CHANGED
|
@@ -10,9 +10,9 @@ in their workflows.
|
|
|
10
10
|
npm install @simplelocalize/cli
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
scripts
|
|
13
|
+
The package installs a small `simplelocalize` launcher into `node_modules/.bin`, so it's available for your
|
|
14
|
+
scripts in `package.json`. The launcher downloads the right SimpleLocalize CLI binary for your system
|
|
15
|
+
on first run (or during installation, when install scripts are allowed) and caches it per version.
|
|
16
16
|
|
|
17
17
|
```json
|
|
18
18
|
{
|
|
@@ -20,7 +20,7 @@ scripts in `package.json`.
|
|
|
20
20
|
"version": "1.0.0",
|
|
21
21
|
"private": true,
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@simplelocalize/cli": "^
|
|
23
|
+
"@simplelocalize/cli": "^3.0.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"start": "react-scripts start",
|
|
@@ -28,6 +28,9 @@ scripts in `package.json`.
|
|
|
28
28
|
"sl:upload": "simplelocalize upload",
|
|
29
29
|
"sl:autotranslate": "simplelocalize auto-translate"
|
|
30
30
|
},
|
|
31
|
+
"simplelocalize": {
|
|
32
|
+
"cliVersion": "2.12.0"
|
|
33
|
+
}
|
|
31
34
|
}
|
|
32
35
|
```
|
|
33
36
|
|
|
@@ -43,13 +46,23 @@ globally:
|
|
|
43
46
|
npx @simplelocalize/cli download
|
|
44
47
|
```
|
|
45
48
|
|
|
46
|
-
##
|
|
49
|
+
## Choosing the CLI version
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
The npm package version is independent of the SimpleLocalize CLI version. The CLI version is resolved in this order:
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
1. `SIMPLELOCALIZE_CLI_VERSION` environment variable, e.g. `SIMPLELOCALIZE_CLI_VERSION=2.12.0 npx @simplelocalize/cli download`
|
|
54
|
+
2. `simplelocalize.cliVersion` in the nearest `package.json` that defines it (searched from the current directory upwards)
|
|
55
|
+
3. the default CLI version bundled with the installed package (`cliVersion` in its `package.json`)
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
Use a full version, e.g. `2.12.0`. See [CLI releases](https://github.com/simplelocalize/simplelocalize-cli/releases) for available versions.
|
|
58
|
+
We recommend pinning `simplelocalize.cliVersion` in your project, so every developer and CI run uses the same CLI.
|
|
59
|
+
|
|
60
|
+
Other environment variables:
|
|
61
|
+
|
|
62
|
+
- `SIMPLELOCALIZE_CLI_CACHE_DIR` – where binaries are cached (default: `~/.cache/simplelocalize/cli`, `%LOCALAPPDATA%\simplelocalize\cli` on Windows). Cache this directory in CI to skip downloads.
|
|
63
|
+
- `SIMPLELOCALIZE_CLI_BINARY` – path to an already installed CLI binary; skips the download entirely.
|
|
64
|
+
|
|
65
|
+
## Upgrading from 2.x
|
|
66
|
+
|
|
67
|
+
`@simplelocalize/cli@2.x` versions were tied to the CLI version (`2.10.x` installed CLI `2.10.0`).
|
|
68
|
+
Since `3.0.0`, pin the CLI version with `simplelocalize.cliVersion` instead. The launcher requires Node.js 18 or newer.
|
package/index.cjs
CHANGED
|
@@ -1,164 +1,159 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const {
|
|
2
|
+
const { spawnSync } = require("child_process");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const axios = require('axios');
|
|
6
6
|
const os = require('os');
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
const VERSION_PATTERN = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
|
|
9
|
+
|
|
10
|
+
const isValidVersion = (version) => typeof version === 'string' && VERSION_PATTERN.test(version);
|
|
11
|
+
|
|
12
|
+
// Walks up from the project directory and returns the first "simplelocalize.cliVersion" found in package.json.
|
|
13
|
+
const findPinnedVersion = (startDir) => {
|
|
14
|
+
let dir = path.resolve(startDir);
|
|
15
|
+
while (true) {
|
|
16
|
+
const packageJsonPath = path.join(dir, 'package.json');
|
|
17
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
18
|
+
try {
|
|
19
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
20
|
+
const cliVersion = packageJson.simplelocalize && packageJson.simplelocalize.cliVersion;
|
|
21
|
+
if (cliVersion) {
|
|
22
|
+
return { version: String(cliVersion), source: packageJsonPath };
|
|
23
|
+
}
|
|
24
|
+
} catch (error) {
|
|
25
|
+
// ignore unreadable package.json and keep looking
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const parent = path.dirname(dir);
|
|
29
|
+
if (parent === dir) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
dir = parent;
|
|
33
|
+
}
|
|
17
34
|
};
|
|
18
35
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
36
|
+
const resolveCliVersion = (projectDir) => {
|
|
37
|
+
const fromEnv = process.env.SIMPLELOCALIZE_CLI_VERSION;
|
|
38
|
+
const resolved = fromEnv
|
|
39
|
+
? { version: fromEnv, source: 'SIMPLELOCALIZE_CLI_VERSION' }
|
|
40
|
+
: findPinnedVersion(projectDir) || { version: require('./package.json').cliVersion, source: 'default' };
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
console.log(execSync(binaryPath + ' --version').toString());
|
|
42
|
+
if (!isValidVersion(resolved.version)) {
|
|
43
|
+
throw new Error(`Invalid SimpleLocalize CLI version '${resolved.version}' (from ${resolved.source}), use a full version, e.g. 2.12.0`);
|
|
33
44
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
45
|
+
return resolved.version;
|
|
46
|
+
};
|
|
39
47
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
const getPlatformSuffix = () => {
|
|
49
|
+
const arch = os.arch() === "arm64" ? "-arm64" : "";
|
|
50
|
+
switch (os.platform()) {
|
|
51
|
+
case "win32":
|
|
52
|
+
return "windows.exe";
|
|
53
|
+
case "darwin":
|
|
54
|
+
return "mac" + arch;
|
|
55
|
+
case "linux":
|
|
56
|
+
return "linux" + arch;
|
|
57
|
+
default:
|
|
58
|
+
throw new Error(`Unsupported platform: ${os.platform()} ${os.arch()}, set SIMPLELOCALIZE_CLI_BINARY to a binary path or use the JAR`);
|
|
44
59
|
}
|
|
60
|
+
};
|
|
45
61
|
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
const getCacheDir = () => {
|
|
63
|
+
if (process.env.SIMPLELOCALIZE_CLI_CACHE_DIR) {
|
|
64
|
+
return process.env.SIMPLELOCALIZE_CLI_CACHE_DIR;
|
|
48
65
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
platform = "linux"
|
|
66
|
+
if (os.platform() === 'win32' && process.env.LOCALAPPDATA) {
|
|
67
|
+
return path.join(process.env.LOCALAPPDATA, 'simplelocalize', 'cli');
|
|
52
68
|
}
|
|
69
|
+
const xdgCache = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
70
|
+
return path.join(xdgCache, 'simplelocalize', 'cli');
|
|
71
|
+
};
|
|
53
72
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
console.log(`Downloading SimpleLocalize CLI ${version} for ${platform}${arch}...`);
|
|
59
|
-
return `https://get.simplelocalize.io/binaries/${version}/simplelocalize-cli-${platform}${arch}`
|
|
60
|
-
}
|
|
73
|
+
const getBinaryPath = (version) => {
|
|
74
|
+
const fileName = 'simplelocalize-cli' + (os.platform() === 'win32' ? '.exe' : '');
|
|
75
|
+
return path.join(getCacheDir(), version, fileName);
|
|
76
|
+
};
|
|
61
77
|
|
|
62
|
-
async function installBinary() {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
fs.unlinkSync(binaryPath);
|
|
67
|
-
}
|
|
68
|
-
const downloadUrl = buildDownloadBinaryUrl(cliVersion);
|
|
69
|
-
if (!fs.existsSync(path.join(__dirname, 'bin'))) {
|
|
70
|
-
fs.mkdirSync(path.join(__dirname, 'bin'));
|
|
71
|
-
}
|
|
78
|
+
async function installBinary(version, binaryPath) {
|
|
79
|
+
const downloadUrl = `https://get.simplelocalize.io/binaries/${version}/simplelocalize-cli-${getPlatformSuffix()}`;
|
|
80
|
+
console.error(`Downloading SimpleLocalize CLI ${version} from ${downloadUrl}...`);
|
|
81
|
+
fs.mkdirSync(path.dirname(binaryPath), { recursive: true });
|
|
72
82
|
|
|
83
|
+
// Download to a temporary file and rename, so parallel runs never see a partial binary
|
|
84
|
+
const tempPath = `${binaryPath}.${process.pid}.tmp`;
|
|
73
85
|
try {
|
|
74
86
|
const response = await axios({
|
|
75
87
|
url: downloadUrl,
|
|
76
88
|
method: 'GET',
|
|
77
89
|
responseType: 'stream'
|
|
78
90
|
});
|
|
79
|
-
|
|
80
|
-
const writer = fs.createWriteStream(binaryPath);
|
|
91
|
+
const writer = fs.createWriteStream(tempPath);
|
|
81
92
|
response.data.pipe(writer);
|
|
82
93
|
await new Promise((resolve, reject) => {
|
|
83
94
|
writer.on('finish', resolve);
|
|
84
95
|
writer.on('error', reject);
|
|
96
|
+
response.data.on('error', reject);
|
|
85
97
|
});
|
|
98
|
+
if (os.platform() !== 'win32') {
|
|
99
|
+
fs.chmodSync(tempPath, 0o755);
|
|
100
|
+
}
|
|
101
|
+
fs.renameSync(tempPath, binaryPath);
|
|
86
102
|
} catch (error) {
|
|
87
|
-
|
|
88
|
-
|
|
103
|
+
fs.rmSync(tempPath, { force: true });
|
|
104
|
+
try {
|
|
105
|
+
fs.rmdirSync(path.dirname(binaryPath));
|
|
106
|
+
} catch (ignored) {
|
|
107
|
+
// not empty, another run already installed this version
|
|
108
|
+
}
|
|
109
|
+
const status = error.response && error.response.status;
|
|
110
|
+
const reason = status === 404 ? `version ${version} not found` : error.message;
|
|
111
|
+
throw new Error(`Error downloading SimpleLocalize CLI binary: ${reason}`);
|
|
89
112
|
}
|
|
113
|
+
}
|
|
90
114
|
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
115
|
+
const ensureBinary = async (projectDir) => {
|
|
116
|
+
if (process.env.SIMPLELOCALIZE_CLI_BINARY) {
|
|
117
|
+
return process.env.SIMPLELOCALIZE_CLI_BINARY;
|
|
95
118
|
}
|
|
96
|
-
|
|
97
|
-
|
|
119
|
+
const version = resolveCliVersion(projectDir);
|
|
120
|
+
const binaryPath = getBinaryPath(version);
|
|
98
121
|
if (!fs.existsSync(binaryPath)) {
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const linkToNodeModulesBin = () => {
|
|
104
|
-
let nodeModulesBinPath = path.join(__dirname, '..', '..', '.bin', 'simplelocalize');
|
|
105
|
-
|
|
106
|
-
if (os.platform() === 'win32') {
|
|
107
|
-
nodeModulesBinPath += '.exe';
|
|
122
|
+
await installBinary(version, binaryPath);
|
|
108
123
|
}
|
|
124
|
+
return binaryPath;
|
|
125
|
+
};
|
|
109
126
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
const run = async () => {
|
|
128
|
+
let binaryPath;
|
|
129
|
+
try {
|
|
130
|
+
binaryPath = await ensureBinary(process.cwd());
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(error.message);
|
|
133
|
+
process.exit(1);
|
|
114
134
|
}
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
|
|
135
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
|
|
136
|
+
if (result.error) {
|
|
137
|
+
console.error(`Error running SimpleLocalize CLI (${binaryPath}): ${result.error.message}`);
|
|
138
|
+
process.exit(1);
|
|
118
139
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const init = async () => {
|
|
124
|
-
console.log("Checking if SimpleLocalize CLI is installed...");
|
|
125
|
-
if (!isBinaryInstalled()) {
|
|
126
|
-
console.log("SimpleLocalize CLI not installed. Installing...");
|
|
127
|
-
await installBinary();
|
|
128
|
-
if (!isBinaryInstalled()) {
|
|
129
|
-
console.error('Error installing SimpleLocalize CLI');
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
console.log("SimpleLocalize CLI installed successfully!");
|
|
133
|
-
} else {
|
|
134
|
-
console.log("SimpleLocalize CLI already installed.");
|
|
140
|
+
if (result.signal) {
|
|
141
|
+
process.kill(process.pid, result.signal);
|
|
135
142
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
process.exit(0);
|
|
139
|
-
}
|
|
143
|
+
process.exit(result.status ?? 1);
|
|
144
|
+
};
|
|
140
145
|
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
(async () => {
|
|
144
|
-
await init();
|
|
145
|
-
})();
|
|
146
|
-
} else {
|
|
147
|
-
// Forward all other CLI commands to the downloaded binary
|
|
148
|
-
if (!isBinaryInstalled()) {
|
|
149
|
-
console.error('SimpleLocalize CLI binary is not installed. Please run: npm install');
|
|
150
|
-
process.exit(1);
|
|
151
|
-
}
|
|
152
|
-
// Build the command to forward
|
|
153
|
-
const args = process.argv.slice(2).map(arg => {
|
|
154
|
-
// Quote arguments with spaces for safety
|
|
155
|
-
if (/\s/.test(arg)) return `"${arg}"`;
|
|
156
|
-
return arg;
|
|
157
|
-
}).join(' ');
|
|
146
|
+
// Optional prefetch during installation; never fails the install (the binary is downloaded on first run anyway)
|
|
147
|
+
const prefetch = async () => {
|
|
158
148
|
try {
|
|
159
|
-
|
|
160
|
-
process.exit(0);
|
|
149
|
+
await ensureBinary(process.env.INIT_CWD || process.cwd());
|
|
161
150
|
} catch (error) {
|
|
162
|
-
|
|
151
|
+
console.warn(`${error.message}. SimpleLocalize CLI will be downloaded on first run.`);
|
|
163
152
|
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (process.argv[2] === 'install' && process.env.npm_lifecycle_event === 'postinstall') {
|
|
156
|
+
prefetch();
|
|
157
|
+
} else {
|
|
158
|
+
run();
|
|
164
159
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplelocalize/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"cliVersion": "2.12.0",
|
|
4
5
|
"description": "SimpleLocalize CLI for NPM",
|
|
5
6
|
"main": "index.cjs",
|
|
6
7
|
"type": "module",
|
|
@@ -9,11 +10,17 @@
|
|
|
9
10
|
},
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
12
|
-
"url": "https://github.com/simplelocalize/
|
|
13
|
+
"url": "git+https://github.com/simplelocalize/simplelocalize-cli-npm.git"
|
|
13
14
|
},
|
|
14
15
|
"bin": {
|
|
15
16
|
"simplelocalize": "index.cjs"
|
|
16
17
|
},
|
|
18
|
+
"files": [
|
|
19
|
+
"index.cjs"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
17
24
|
"dependencies": {
|
|
18
25
|
"axios": "^1.8.3"
|
|
19
26
|
},
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
name: CI & Publish
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
workflow_dispatch:
|
|
9
|
-
|
|
10
|
-
jobs:
|
|
11
|
-
test:
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v4
|
|
15
|
-
- uses: actions/setup-node@v4
|
|
16
|
-
with:
|
|
17
|
-
node-version: '20'
|
|
18
|
-
cache: 'npm'
|
|
19
|
-
- run: npm ci
|
|
20
|
-
|
|
21
|
-
publish:
|
|
22
|
-
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
23
|
-
needs: test
|
|
24
|
-
runs-on: ubuntu-latest
|
|
25
|
-
steps:
|
|
26
|
-
- uses: actions/checkout@v4
|
|
27
|
-
- uses: actions/setup-node@v4
|
|
28
|
-
with:
|
|
29
|
-
node-version: '20'
|
|
30
|
-
registry-url: 'https://registry.npmjs.org/'
|
|
31
|
-
- run: npm ci
|
|
32
|
-
- run: npm run build --if-present
|
|
33
|
-
- run: npm publish --access public
|
|
34
|
-
env:
|
|
35
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|