@neon-rs/cli 0.0.1 → 0.0.3
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/dist/cargo.js +14 -19
- package/dist/command.js +39 -0
- package/dist/commands/dist.js +60 -23
- package/dist/commands/help.js +26 -0
- package/dist/commands/install-builds.js +65 -0
- package/dist/commands/pack-build.js +106 -0
- package/dist/index.js +22 -41
- package/dist/print.js +69 -0
- package/package.json +1 -1
- package/types/cargo.d.ts +4 -3
- package/types/command.d.ts +24 -0
- package/types/commands/cross-dist.d.ts +14 -1
- package/types/commands/cross-install.d.ts +10 -1
- package/types/commands/cross-pack.d.ts +12 -1
- package/types/commands/dist.d.ts +16 -1
- package/types/commands/help.d.ts +10 -0
- package/types/commands/install-builds.d.ts +10 -0
- package/types/commands/pack-build.d.ts +13 -0
- package/types/index.d.ts +1 -2
- package/types/print.d.ts +5 -0
- package/types/usage.d.ts +0 -1
- package/dist/commands/cross-dist.js +0 -40
- package/dist/commands/cross-install.js +0 -42
- package/dist/commands/cross-pack.js +0 -81
- package/dist/usage.js +0 -150
package/dist/cargo.js
CHANGED
|
@@ -119,33 +119,28 @@ export class MessageStream {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
-
export class
|
|
123
|
-
constructor(
|
|
122
|
+
export class UnmountMessageStream extends MessageStream {
|
|
123
|
+
constructor(mount, manifestPath, file) {
|
|
124
124
|
super(file);
|
|
125
|
-
this.
|
|
125
|
+
this._mount = mount;
|
|
126
|
+
this._manifestPath = manifestPath;
|
|
126
127
|
}
|
|
127
128
|
async findPath(pred) {
|
|
128
129
|
// The base class's version reports paths as absolute paths from within
|
|
129
|
-
//
|
|
130
|
-
const
|
|
131
|
-
if (!
|
|
130
|
+
// a mount in a virtual filesystem.
|
|
131
|
+
const mountedPath = await super.findPath(pred);
|
|
132
|
+
if (!mountedPath) {
|
|
132
133
|
return null;
|
|
133
134
|
}
|
|
134
|
-
// Convert the absolute path into a relative path from within the
|
|
135
|
-
// workspace's target directory.
|
|
136
|
-
const cross = await execa('cross', ['metadata', '--format-version=1', '--no-deps'], {
|
|
137
|
-
cwd: this._dir,
|
|
138
|
-
shell: true
|
|
139
|
-
});
|
|
140
|
-
if (cross.exitCode !== 0) {
|
|
141
|
-
throw new Error(`Invoking \`cross metadata\` failed: ${cross.stderr}`);
|
|
142
|
-
}
|
|
143
|
-
const crossMetadata = JSON.parse(cross.stdout);
|
|
144
135
|
// The path relative to the workspace's target directory.
|
|
145
|
-
const relPath = path.relative(
|
|
136
|
+
const relPath = path.relative(this._mount, mountedPath);
|
|
146
137
|
// Now find the true absolute path of the target directory on the host system.
|
|
147
|
-
const cargo = await execa('cargo', [
|
|
148
|
-
|
|
138
|
+
const cargo = await execa('cargo', [
|
|
139
|
+
'metadata',
|
|
140
|
+
'--format-version=1',
|
|
141
|
+
'--no-deps',
|
|
142
|
+
...(this._manifestPath ? ['--manifest-path', this._manifestPath] : [])
|
|
143
|
+
], {
|
|
149
144
|
shell: true
|
|
150
145
|
});
|
|
151
146
|
if (cargo.exitCode !== 0) {
|
package/dist/command.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Dist from './commands/dist.js';
|
|
2
|
+
import PackBuild from './commands/pack-build.js';
|
|
3
|
+
import InstallBuilds from './commands/install-builds.js';
|
|
4
|
+
import Help from './commands/help.js';
|
|
5
|
+
export var CommandName;
|
|
6
|
+
(function (CommandName) {
|
|
7
|
+
CommandName["Help"] = "help";
|
|
8
|
+
CommandName["Dist"] = "dist";
|
|
9
|
+
CommandName["PackBuild"] = "pack-build";
|
|
10
|
+
CommandName["InstallBuilds"] = "install-builds";
|
|
11
|
+
})(CommandName = CommandName || (CommandName = {}));
|
|
12
|
+
;
|
|
13
|
+
export function isCommandName(s) {
|
|
14
|
+
const keys = Object.values(CommandName);
|
|
15
|
+
return keys.includes(s);
|
|
16
|
+
}
|
|
17
|
+
export function asCommandName(name) {
|
|
18
|
+
if (!isCommandName(name)) {
|
|
19
|
+
throw new RangeError(`Command not recognized: ${name}`);
|
|
20
|
+
}
|
|
21
|
+
return name;
|
|
22
|
+
}
|
|
23
|
+
const COMMANDS = {
|
|
24
|
+
[CommandName.Help]: Help,
|
|
25
|
+
[CommandName.Dist]: Dist,
|
|
26
|
+
[CommandName.PackBuild]: PackBuild,
|
|
27
|
+
[CommandName.InstallBuilds]: InstallBuilds
|
|
28
|
+
};
|
|
29
|
+
export function commandFor(name) {
|
|
30
|
+
return COMMANDS[name];
|
|
31
|
+
}
|
|
32
|
+
export function summaries() {
|
|
33
|
+
return [
|
|
34
|
+
{ name: CommandName.Help, summary: Help.summary() },
|
|
35
|
+
{ name: CommandName.Dist, summary: Dist.summary() },
|
|
36
|
+
{ name: CommandName.PackBuild, summary: PackBuild.summary() },
|
|
37
|
+
{ name: CommandName.InstallBuilds, summary: InstallBuilds.summary() }
|
|
38
|
+
];
|
|
39
|
+
}
|
package/dist/commands/dist.js
CHANGED
|
@@ -1,38 +1,75 @@
|
|
|
1
1
|
import { copyFile } from 'node:fs/promises';
|
|
2
2
|
import commandLineArgs from 'command-line-args';
|
|
3
|
-
import { MessageStream, isCompilerArtifact } from '../cargo.js';
|
|
3
|
+
import { MessageStream, UnmountMessageStream, isCompilerArtifact } from '../cargo.js';
|
|
4
4
|
// FIXME: add options to infer crate name from manifests
|
|
5
5
|
// --package <path/to/package.json>
|
|
6
6
|
// --crate <path/to/Cargo.toml>
|
|
7
7
|
const OPTIONS = [
|
|
8
8
|
{ name: 'name', alias: 'n', type: String, defaultValue: null },
|
|
9
|
-
{ name: 'log', alias: 'l', type: String, defaultValue: null },
|
|
10
9
|
{ name: 'file', alias: 'f', type: String, defaultValue: null },
|
|
10
|
+
{ name: 'log', alias: 'l', type: String, defaultValue: null },
|
|
11
|
+
{ name: 'mount', alias: 'm', type: String, defaultValue: null },
|
|
12
|
+
{ name: 'manifest-path', type: String, defaultValue: null },
|
|
11
13
|
{ name: 'out', alias: 'o', type: String, defaultValue: 'index.node' }
|
|
12
14
|
];
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
export default class Dist {
|
|
16
|
+
static summary() { return 'Generate a .node file from a build.'; }
|
|
17
|
+
static syntax() { return 'neon dist [-n <name>] [-f <dylib>|[-l <log>] [-m <path>]] [-o <dist>]'; }
|
|
18
|
+
static options() {
|
|
19
|
+
return [
|
|
20
|
+
{ name: '-n, --name', summary: 'Crate name. (Default: $npm_package_name)' },
|
|
21
|
+
{ name: '-f, --file <dylib>', summary: 'Build .node from dylib file <dylib>.' },
|
|
22
|
+
{ name: '-l, --log <log>', summary: 'Find dylib path from cargo messages <log>. (Default: stdin)' },
|
|
23
|
+
{
|
|
24
|
+
name: '-m, --mount <path>',
|
|
25
|
+
summary: 'Mounted path of target directory in virtual filesystem. This is used to map paths from the log data back to their real paths, needed when tools such as cross-rs report messages from within a mounted Docker filesystem.'
|
|
26
|
+
},
|
|
27
|
+
{ name: '--manifest-path <path>', summary: 'Real path to Cargo.toml. (Default: cargo behavior)' },
|
|
28
|
+
{ name: '-o, --out <dist>', summary: 'Copy output to file <dist>. (Default: index.node)' }
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
static seeAlso() {
|
|
32
|
+
return [
|
|
33
|
+
{ name: 'cargo messages', summary: '<https://doc.rust-lang.org/cargo/reference/external-tools.html>' },
|
|
34
|
+
{ name: 'cross-rs', summary: '<https://github.com/cross-rs/cross>' }
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
constructor(argv) {
|
|
38
|
+
const options = commandLineArgs(OPTIONS, { argv });
|
|
39
|
+
if (options.log && options.file) {
|
|
40
|
+
throw new Error("Options --log and --file cannot both be enabled.");
|
|
18
41
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
if (options.file && options.mount) {
|
|
43
|
+
throw new Error("Options --mount and --file cannot both be enabled.");
|
|
44
|
+
}
|
|
45
|
+
if (options['manifest-path'] && !options.mount) {
|
|
46
|
+
throw new Error("Option --manifest-path requires option --mount to be provided.");
|
|
47
|
+
}
|
|
48
|
+
this._log = options.log ?? null;
|
|
49
|
+
this._file = options.file ?? null;
|
|
50
|
+
this._mount = options.mount;
|
|
51
|
+
this._manifestPath = options['manifest-path'];
|
|
52
|
+
this._crateName = options.name || process.env['npm_package_name'];
|
|
53
|
+
this._out = options.out;
|
|
27
54
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
55
|
+
async findArtifact() {
|
|
56
|
+
const stream = this._mount
|
|
57
|
+
? new UnmountMessageStream(this._mount, this._manifestPath, this._log)
|
|
58
|
+
: new MessageStream(this._log);
|
|
59
|
+
return await stream.findPath((msg) => {
|
|
60
|
+
if (!isCompilerArtifact(msg) || (msg.target.name !== this._crateName)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const index = msg.target.crate_types.indexOf('cdylib');
|
|
64
|
+
return (index < 0) ? null : msg.filenames[index];
|
|
65
|
+
});
|
|
31
66
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
67
|
+
async run() {
|
|
68
|
+
const file = this._file || await this.findArtifact();
|
|
69
|
+
if (!file) {
|
|
70
|
+
throw new Error(`No library found for crate ${this._crateName}`);
|
|
71
|
+
}
|
|
72
|
+
// FIXME: needs all the logic of cargo-cp-artifact (timestamp check, M1 workaround, async, errors)
|
|
73
|
+
await copyFile(file, this._out);
|
|
35
74
|
}
|
|
36
|
-
// FIXME: needs all the logic of cargo-cp-artifact (timestamp check, M1 workaround, async, errors)
|
|
37
|
-
await copyFile(file, options.out);
|
|
38
75
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { printMainUsage, printCommandUsage } from '../print.js';
|
|
2
|
+
import { asCommandName } from '../command.js';
|
|
3
|
+
export default class Help {
|
|
4
|
+
static summary() { return 'Display help information about Neon.'; }
|
|
5
|
+
static syntax() { return 'neon help <command>'; }
|
|
6
|
+
static options() {
|
|
7
|
+
return [
|
|
8
|
+
{ name: '<command>', summary: 'Command to display help information about.' }
|
|
9
|
+
];
|
|
10
|
+
}
|
|
11
|
+
static seeAlso() { }
|
|
12
|
+
constructor(argv) {
|
|
13
|
+
this._name = argv.length > 0 ? asCommandName(argv[0]) : undefined;
|
|
14
|
+
if (argv.length > 1) {
|
|
15
|
+
throw new Error(`Unexpected argument: ${argv[1]}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async run() {
|
|
19
|
+
if (this._name) {
|
|
20
|
+
printCommandUsage(this._name);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
printMainUsage();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import commandLineArgs from 'command-line-args';
|
|
3
|
+
import * as fs from 'node:fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
const OPTIONS = [
|
|
6
|
+
{ name: 'bundle', alias: 'b', type: String, defaultValue: null },
|
|
7
|
+
{ name: 'no-bundle', alias: 'B', type: String, defaultValue: null }
|
|
8
|
+
];
|
|
9
|
+
export default class InstallBuilds {
|
|
10
|
+
static summary() { return 'Install dependencies on prebuilds in package.json.'; }
|
|
11
|
+
static syntax() { return 'neon install-builds [-b <file>|-B]'; }
|
|
12
|
+
static options() {
|
|
13
|
+
return [
|
|
14
|
+
{ name: '-b, --bundle <file>', summary: 'File to generate bundling metadata. (Default: .targets)' },
|
|
15
|
+
{
|
|
16
|
+
name: '',
|
|
17
|
+
summary: 'This generated file ensures support for bundlers (e.g. @vercel/ncc), which rely on static analysis to detect and enable any addons used by the library.'
|
|
18
|
+
},
|
|
19
|
+
{ name: '-B, --no-bundle', summary: 'Do not generate bundling metadata.' }
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
static seeAlso() {
|
|
23
|
+
return [
|
|
24
|
+
{ name: 'ncc', summary: '<https://github.com/vercel/ncc>' }
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
constructor(argv) {
|
|
28
|
+
const options = commandLineArgs(OPTIONS, { argv });
|
|
29
|
+
if (options.bundle && options['no-bundle']) {
|
|
30
|
+
throw new Error("Options --bundle and --no-bundle cannot both be enabled.");
|
|
31
|
+
}
|
|
32
|
+
this._bundle = options['no-bundle']
|
|
33
|
+
? null
|
|
34
|
+
: !options.bundle
|
|
35
|
+
? '.targets'
|
|
36
|
+
: options.bundle;
|
|
37
|
+
}
|
|
38
|
+
async run() {
|
|
39
|
+
const manifest = JSON.parse(await fs.readFile(path.join(process.cwd(), 'package.json'), { encoding: 'utf8' }));
|
|
40
|
+
const version = manifest.version;
|
|
41
|
+
const targets = Object.values(manifest.neon.targets);
|
|
42
|
+
const specs = targets.map(name => `${name}@${version}`);
|
|
43
|
+
const result = await execa('npm', ['install', '--save-exact', '-O', ...specs], { shell: true });
|
|
44
|
+
if (result.exitCode !== 0) {
|
|
45
|
+
console.error(result.stderr);
|
|
46
|
+
process.exit(result.exitCode);
|
|
47
|
+
}
|
|
48
|
+
if (!this._bundle) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const PREAMBLE = `// AUTOMATICALLY GENERATED FILE. DO NOT EDIT.
|
|
52
|
+
//
|
|
53
|
+
// This code is never executed but is detected by the static analysis of
|
|
54
|
+
// bundlers such as \`@vercel/ncc\`. The require() expression that selects
|
|
55
|
+
// the right binary module for the current platform is too dynamic to be
|
|
56
|
+
// analyzable by bundler analyses, so this module provides an exhaustive
|
|
57
|
+
// static list for those analyses.
|
|
58
|
+
|
|
59
|
+
return;
|
|
60
|
+
|
|
61
|
+
`;
|
|
62
|
+
const requires = targets.map(name => `require('${name}');`).join('\n');
|
|
63
|
+
await fs.writeFile(this._bundle, PREAMBLE + requires + '\n');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import * as temp from 'temp';
|
|
5
|
+
import commandLineArgs from 'command-line-args';
|
|
6
|
+
import { execa } from 'execa';
|
|
7
|
+
const mktemp = temp.track().mkdir;
|
|
8
|
+
const OPTIONS = [
|
|
9
|
+
{ name: 'file', alias: 'f', type: String, defaultValue: 'index.node' },
|
|
10
|
+
{ name: 'out-dir', alias: 'd', type: String, defaultValue: null }
|
|
11
|
+
];
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const LLVM = require('../../data/llvm.json');
|
|
14
|
+
const NODE = require('../../data/node.json');
|
|
15
|
+
function lookup(target) {
|
|
16
|
+
const path = LLVM[target];
|
|
17
|
+
if (!path) {
|
|
18
|
+
throw new Error(`Rust target ${target} not supported`);
|
|
19
|
+
}
|
|
20
|
+
const [platform, arch, abi] = path;
|
|
21
|
+
return NODE[platform][arch][abi];
|
|
22
|
+
}
|
|
23
|
+
export default class PackBuild {
|
|
24
|
+
static summary() { return 'Create an npm tarball from a prebuild.'; }
|
|
25
|
+
static syntax() { return 'neon pack-build [-f <addon>] [-t <target>]'; }
|
|
26
|
+
static options() {
|
|
27
|
+
return [
|
|
28
|
+
{ name: '-f, --file <addon>', summary: 'Prebuilt .node file to pack. (Default: index.node)' },
|
|
29
|
+
{ name: '-t, --target <target>', summary: 'Rust target triple the addon was built for. (Default: rustc default host)' }
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
static seeAlso() {
|
|
33
|
+
return [
|
|
34
|
+
{ name: 'Rust platform support', summary: '<https://doc.rust-lang.org/rustc/platform-support.html>' },
|
|
35
|
+
{ name: 'npm pack', summary: '<https://docs.npmjs.com/cli/commands/npm-pack>' },
|
|
36
|
+
{ name: 'cross-rs', summary: '<https://github.com/cross-rs/cross>' }
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
constructor(argv) {
|
|
40
|
+
const options = commandLineArgs(OPTIONS, { argv });
|
|
41
|
+
this._target = options.target || null;
|
|
42
|
+
this._addon = options.file;
|
|
43
|
+
this._outDir = options['out-dir'] || path.join(process.cwd(), 'dist');
|
|
44
|
+
}
|
|
45
|
+
async currentTarget() {
|
|
46
|
+
const result = await execa("rustc", ["-vV"], { shell: true });
|
|
47
|
+
if (result.exitCode !== 0) {
|
|
48
|
+
throw new Error(`Could not determine current Rust target: ${result.stderr}`);
|
|
49
|
+
}
|
|
50
|
+
const hostLine = result.stdout.split(/\n/).find(line => line.startsWith('host:'));
|
|
51
|
+
if (!hostLine) {
|
|
52
|
+
throw new Error("Could not determine current Rust target (unexpected rustc output)");
|
|
53
|
+
}
|
|
54
|
+
return hostLine.replace(/^host:\s+/, '');
|
|
55
|
+
}
|
|
56
|
+
async run() {
|
|
57
|
+
await fs.mkdir(this._outDir, { recursive: true });
|
|
58
|
+
const manifest = JSON.parse(await fs.readFile('package.json', { encoding: 'utf8' }));
|
|
59
|
+
const version = manifest.version;
|
|
60
|
+
const targets = manifest.neon.targets;
|
|
61
|
+
const target = this._target || await this.currentTarget();
|
|
62
|
+
const name = targets[target];
|
|
63
|
+
if (!name) {
|
|
64
|
+
throw new Error(`Rust target ${target} not found in package.json.`);
|
|
65
|
+
}
|
|
66
|
+
const targetInfo = lookup(target);
|
|
67
|
+
const description = `Prebuilt binary package for \`${manifest.name}\` on \`${targetInfo.node}\`.`;
|
|
68
|
+
let prebuildManifest = {
|
|
69
|
+
name,
|
|
70
|
+
description,
|
|
71
|
+
version,
|
|
72
|
+
os: [targetInfo.platform],
|
|
73
|
+
cpu: [targetInfo.arch],
|
|
74
|
+
main: "index.node",
|
|
75
|
+
files: ["README.md", "index.node"]
|
|
76
|
+
};
|
|
77
|
+
const OPTIONAL_KEYS = [
|
|
78
|
+
'author', 'repository', 'keywords', 'bugs', 'homepage', 'license', 'engines'
|
|
79
|
+
];
|
|
80
|
+
for (const key of OPTIONAL_KEYS) {
|
|
81
|
+
if (manifest[key]) {
|
|
82
|
+
prebuildManifest[key] = manifest[key];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const tmpdir = await mktemp('neon-');
|
|
86
|
+
await fs.writeFile(path.join(tmpdir, "package.json"), JSON.stringify(prebuildManifest, null, 2));
|
|
87
|
+
await fs.copyFile(this._addon, path.join(tmpdir, "index.node"));
|
|
88
|
+
await fs.writeFile(path.join(tmpdir, "README.md"), `# \`${name}\`\n\n${description}\n`);
|
|
89
|
+
const result = await execa("npm", ["pack", "--json"], {
|
|
90
|
+
shell: true,
|
|
91
|
+
cwd: tmpdir,
|
|
92
|
+
stdio: ['pipe', 'pipe', 'inherit']
|
|
93
|
+
});
|
|
94
|
+
if (result.exitCode !== 0) {
|
|
95
|
+
process.exit(result.exitCode);
|
|
96
|
+
}
|
|
97
|
+
// FIXME: comment linking to the npm issue this fixes
|
|
98
|
+
const tarball = JSON.parse(result.stdout)[0].filename.replace('@', '').replace('/', '-');
|
|
99
|
+
const dest = path.join(this._outDir, tarball);
|
|
100
|
+
// Copy instead of move since e.g. GitHub Actions Windows runners host temp directories
|
|
101
|
+
// on a different device (which causes fs.renameSync to fail).
|
|
102
|
+
await fs.copyFile(path.join(tmpdir, tarball), dest);
|
|
103
|
+
console.log(dest);
|
|
104
|
+
}
|
|
105
|
+
;
|
|
106
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,52 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import commandLineCommands from 'command-line-commands';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
import { printErrorWithUsage, printError, printMainUsage } from './print.js';
|
|
4
|
+
import { CommandName, asCommandName, commandFor } from './command.js';
|
|
5
|
+
class Cli {
|
|
6
|
+
parse() {
|
|
7
|
+
try {
|
|
8
|
+
const { command, argv } = commandLineCommands([null, ...Object.values(CommandName)]);
|
|
9
|
+
if (!command) {
|
|
10
|
+
printMainUsage();
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
const ctor = commandFor(asCommandName(command));
|
|
14
|
+
return new ctor(argv);
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
printErrorWithUsage(e);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
async function main() {
|
|
23
|
+
const cli = new Cli();
|
|
24
|
+
const command = cli.parse();
|
|
22
25
|
try {
|
|
23
|
-
|
|
24
|
-
switch (command) {
|
|
25
|
-
case 'dist':
|
|
26
|
-
await dist(argv);
|
|
27
|
-
break;
|
|
28
|
-
case 'cross-dist':
|
|
29
|
-
await crossDist(argv);
|
|
30
|
-
break;
|
|
31
|
-
case 'cross-pack':
|
|
32
|
-
await crossPack(argv);
|
|
33
|
-
break;
|
|
34
|
-
case 'cross-install':
|
|
35
|
-
await crossInstall(argv);
|
|
36
|
-
break;
|
|
37
|
-
case 'help':
|
|
38
|
-
if (argv.length > 0) {
|
|
39
|
-
printUsage(expectCommandName(argv[0]));
|
|
40
|
-
process.exit(0);
|
|
41
|
-
}
|
|
42
|
-
// FALL THROUGH
|
|
43
|
-
case null:
|
|
44
|
-
printUsage();
|
|
45
|
-
}
|
|
46
|
-
process.exit(0);
|
|
26
|
+
await command.run();
|
|
47
27
|
}
|
|
48
28
|
catch (e) {
|
|
49
|
-
|
|
29
|
+
printError(e);
|
|
30
|
+
process.exit(1);
|
|
50
31
|
}
|
|
51
32
|
}
|
|
52
33
|
main();
|
package/dist/print.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import commandLineUsage from 'command-line-usage';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { commandFor, summaries } from './command.js';
|
|
4
|
+
function pink(text) {
|
|
5
|
+
return chalk.bold.hex('#e75480')(text);
|
|
6
|
+
}
|
|
7
|
+
function blue(text) {
|
|
8
|
+
return chalk.bold.cyanBright(text);
|
|
9
|
+
}
|
|
10
|
+
function yellow(text) {
|
|
11
|
+
return chalk.bold.yellowBright(text);
|
|
12
|
+
}
|
|
13
|
+
function green(text) {
|
|
14
|
+
return chalk.bold.greenBright(text);
|
|
15
|
+
}
|
|
16
|
+
function commandUsage(name, command) {
|
|
17
|
+
const sections = [
|
|
18
|
+
{
|
|
19
|
+
content: `${pink('Neon:')} ${name} - ${command.summary()}`,
|
|
20
|
+
raw: true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
header: blue('Usage:'),
|
|
24
|
+
content: `${blue('$')} ${command.syntax()}`
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
header: yellow('Options:'),
|
|
28
|
+
content: command.options()
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
const seeAlso = command.seeAlso();
|
|
32
|
+
if (seeAlso) {
|
|
33
|
+
sections.push({ header: green('See Also:'), content: seeAlso });
|
|
34
|
+
}
|
|
35
|
+
return commandLineUsage(sections).trimStart();
|
|
36
|
+
}
|
|
37
|
+
function mainUsage() {
|
|
38
|
+
const sections = [
|
|
39
|
+
{
|
|
40
|
+
content: `${pink('Neon:')} the npm packaging tool for Rust addons`,
|
|
41
|
+
raw: true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
header: blue('Usage:'),
|
|
45
|
+
content: `${blue('$')} neon <command> <options>`
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
header: yellow('Commands:'),
|
|
49
|
+
content: summaries()
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
return commandLineUsage(sections).trim();
|
|
53
|
+
}
|
|
54
|
+
export function printCommandUsage(name) {
|
|
55
|
+
console.error(commandUsage(name, commandFor(name)));
|
|
56
|
+
}
|
|
57
|
+
export function printMainUsage() {
|
|
58
|
+
console.error(mainUsage());
|
|
59
|
+
console.error();
|
|
60
|
+
console.error("See 'neon help <command>' for more information on a specific command.");
|
|
61
|
+
}
|
|
62
|
+
export function printErrorWithUsage(e) {
|
|
63
|
+
console.error(mainUsage());
|
|
64
|
+
console.error();
|
|
65
|
+
printError(e);
|
|
66
|
+
}
|
|
67
|
+
export function printError(e) {
|
|
68
|
+
console.error(chalk.bold.red("error:") + " " + ((e instanceof Error) ? e.message : String(e)));
|
|
69
|
+
}
|
package/package.json
CHANGED
package/types/cargo.d.ts
CHANGED
|
@@ -35,8 +35,9 @@ export declare class MessageStream {
|
|
|
35
35
|
constructor(file?: string | null);
|
|
36
36
|
findPath(pred: MessageFilter<string>): Promise<string | null>;
|
|
37
37
|
}
|
|
38
|
-
export declare class
|
|
39
|
-
private
|
|
40
|
-
|
|
38
|
+
export declare class UnmountMessageStream extends MessageStream {
|
|
39
|
+
private _mount;
|
|
40
|
+
private _manifestPath;
|
|
41
|
+
constructor(mount: string, manifestPath: string | null, file?: string | null);
|
|
41
42
|
findPath(pred: MessageFilter<string>): Promise<string | null>;
|
|
42
43
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface Command {
|
|
2
|
+
run(): Promise<void>;
|
|
3
|
+
}
|
|
4
|
+
export interface CommandStatics {
|
|
5
|
+
summary(): string;
|
|
6
|
+
syntax(): string;
|
|
7
|
+
options(): CommandDetail[];
|
|
8
|
+
seeAlso(): CommandDetail[] | void;
|
|
9
|
+
}
|
|
10
|
+
export type CommandClass = (new (argv: string[]) => Command) & CommandStatics;
|
|
11
|
+
export type CommandDetail = {
|
|
12
|
+
name: string;
|
|
13
|
+
summary: string;
|
|
14
|
+
};
|
|
15
|
+
export declare enum CommandName {
|
|
16
|
+
Help = "help",
|
|
17
|
+
Dist = "dist",
|
|
18
|
+
PackBuild = "pack-build",
|
|
19
|
+
InstallBuilds = "install-builds"
|
|
20
|
+
}
|
|
21
|
+
export declare function isCommandName(s: string): s is CommandName;
|
|
22
|
+
export declare function asCommandName(name: string): CommandName;
|
|
23
|
+
export declare function commandFor(name: CommandName): CommandClass;
|
|
24
|
+
export declare function summaries(): CommandDetail[];
|
|
@@ -1 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { Command, CommandDetail } from '../command.js';
|
|
2
|
+
export default class CrossDist implements Command {
|
|
3
|
+
static summary(): string;
|
|
4
|
+
static syntax(): string;
|
|
5
|
+
static options(): CommandDetail[];
|
|
6
|
+
static seeAlso(): CommandDetail[] | void;
|
|
7
|
+
private _file;
|
|
8
|
+
private _log;
|
|
9
|
+
private _crateName;
|
|
10
|
+
private _dir;
|
|
11
|
+
private _out;
|
|
12
|
+
constructor(argv: string[]);
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -1 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { Command, CommandDetail } from '../command.js';
|
|
2
|
+
export default class CrossInstall implements Command {
|
|
3
|
+
static summary(): string;
|
|
4
|
+
static syntax(): string;
|
|
5
|
+
static options(): CommandDetail[];
|
|
6
|
+
static seeAlso(): CommandDetail[] | void;
|
|
7
|
+
private _bundle;
|
|
8
|
+
constructor(argv: string[]);
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -1 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { Command, CommandDetail } from '../command.js';
|
|
2
|
+
export default class CrossPack implements Command {
|
|
3
|
+
static summary(): string;
|
|
4
|
+
static syntax(): string;
|
|
5
|
+
static options(): CommandDetail[];
|
|
6
|
+
static seeAlso(): CommandDetail[] | void;
|
|
7
|
+
private _target;
|
|
8
|
+
private _addon;
|
|
9
|
+
private _outDir;
|
|
10
|
+
constructor(argv: string[]);
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
package/types/commands/dist.d.ts
CHANGED
|
@@ -1 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { Command, CommandDetail } from '../command.js';
|
|
2
|
+
export default class Dist implements Command {
|
|
3
|
+
static summary(): string;
|
|
4
|
+
static syntax(): string;
|
|
5
|
+
static options(): CommandDetail[];
|
|
6
|
+
static seeAlso(): CommandDetail[] | void;
|
|
7
|
+
private _log;
|
|
8
|
+
private _file;
|
|
9
|
+
private _mount;
|
|
10
|
+
private _manifestPath;
|
|
11
|
+
private _crateName;
|
|
12
|
+
private _out;
|
|
13
|
+
constructor(argv: string[]);
|
|
14
|
+
findArtifact(): Promise<string | null>;
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command, CommandDetail } from '../command.js';
|
|
2
|
+
export default class Help implements Command {
|
|
3
|
+
static summary(): string;
|
|
4
|
+
static syntax(): string;
|
|
5
|
+
static options(): CommandDetail[];
|
|
6
|
+
static seeAlso(): CommandDetail[] | void;
|
|
7
|
+
private _name?;
|
|
8
|
+
constructor(argv: string[]);
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command, CommandDetail } from '../command.js';
|
|
2
|
+
export default class InstallBuilds implements Command {
|
|
3
|
+
static summary(): string;
|
|
4
|
+
static syntax(): string;
|
|
5
|
+
static options(): CommandDetail[];
|
|
6
|
+
static seeAlso(): CommandDetail[] | void;
|
|
7
|
+
private _bundle;
|
|
8
|
+
constructor(argv: string[]);
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command, CommandDetail } from '../command.js';
|
|
2
|
+
export default class PackBuild implements Command {
|
|
3
|
+
static summary(): string;
|
|
4
|
+
static syntax(): string;
|
|
5
|
+
static options(): CommandDetail[];
|
|
6
|
+
static seeAlso(): CommandDetail[] | void;
|
|
7
|
+
private _target;
|
|
8
|
+
private _addon;
|
|
9
|
+
private _outDir;
|
|
10
|
+
constructor(argv: string[]);
|
|
11
|
+
currentTarget(): Promise<string>;
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
package/types/index.d.ts
CHANGED
package/types/print.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { CommandName } from './command.js';
|
|
2
|
+
export declare function printCommandUsage(name: CommandName): void;
|
|
3
|
+
export declare function printMainUsage(): void;
|
|
4
|
+
export declare function printErrorWithUsage(e: any): void;
|
|
5
|
+
export declare function printError(e: any): void;
|
package/types/usage.d.ts
CHANGED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { copyFile } from 'node:fs/promises';
|
|
2
|
-
import commandLineArgs from 'command-line-args';
|
|
3
|
-
import { CrossMessageStream, isCompilerArtifact } from '../cargo.js';
|
|
4
|
-
// FIXME: add options to infer crate name from manifests
|
|
5
|
-
// --package <path/to/package.json>
|
|
6
|
-
// --crate <path/to/Cargo.toml>
|
|
7
|
-
const OPTIONS = [
|
|
8
|
-
{ name: 'name', alias: 'n', type: String, defaultValue: null },
|
|
9
|
-
{ name: 'log', alias: 'l', type: String, defaultValue: null },
|
|
10
|
-
{ name: 'dir', alias: 'd', type: String, defaultValue: null },
|
|
11
|
-
{ name: 'file', alias: 'f', type: String, defaultValue: null },
|
|
12
|
-
{ name: 'out', alias: 'o', type: String, defaultValue: 'index.node' }
|
|
13
|
-
];
|
|
14
|
-
async function findArtifact(log, crateName, dir) {
|
|
15
|
-
const stream = new CrossMessageStream(dir, log);
|
|
16
|
-
return await stream.findPath((msg) => {
|
|
17
|
-
if (!isCompilerArtifact(msg) || (msg.target.name !== crateName)) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
const index = msg.target.crate_types.indexOf('cdylib');
|
|
21
|
-
return (index < 0) ? null : msg.filenames[index];
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
export default async function main(argv) {
|
|
25
|
-
const options = commandLineArgs(OPTIONS, { argv });
|
|
26
|
-
if (options.log && options.file) {
|
|
27
|
-
throw new Error("Options --log and --file cannot both be enabled.");
|
|
28
|
-
}
|
|
29
|
-
const crateName = options.name || process.env['npm_package_name'];
|
|
30
|
-
if (!crateName) {
|
|
31
|
-
throw new Error("No crate name provided.");
|
|
32
|
-
}
|
|
33
|
-
const dir = options.dir || process.cwd();
|
|
34
|
-
const file = options.file ?? await findArtifact(options.log, crateName, dir);
|
|
35
|
-
if (!file) {
|
|
36
|
-
throw new Error(`No library found for crate ${crateName}`);
|
|
37
|
-
}
|
|
38
|
-
// FIXME: needs all the logic of cargo-cp-artifact (timestamp check, M1 workaround, async, errors)
|
|
39
|
-
await copyFile(file, options.out);
|
|
40
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { execa } from 'execa';
|
|
2
|
-
import commandLineArgs from 'command-line-args';
|
|
3
|
-
import * as fs from 'node:fs/promises';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
const OPTIONS = [
|
|
6
|
-
{ name: 'bundle', alias: 'b', type: String, defaultValue: null },
|
|
7
|
-
{ name: 'no-bundle', alias: 'B', type: String, defaultValue: null }
|
|
8
|
-
];
|
|
9
|
-
export default async function main(argv) {
|
|
10
|
-
const options = commandLineArgs(OPTIONS, { argv });
|
|
11
|
-
if (options.bundle && options['no-bundle']) {
|
|
12
|
-
throw new Error("Options --bundle and --no-bundle cannot both be enabled.");
|
|
13
|
-
}
|
|
14
|
-
if (!options.bundle && !options['no-bundle']) {
|
|
15
|
-
options.bundle = '.targets';
|
|
16
|
-
}
|
|
17
|
-
const manifest = JSON.parse(await fs.readFile(path.join(process.cwd(), 'package.json'), { encoding: 'utf8' }));
|
|
18
|
-
const version = manifest.version;
|
|
19
|
-
const targets = Object.values(manifest.neon.targets);
|
|
20
|
-
const specs = targets.map(name => `${name}@${version}`);
|
|
21
|
-
const result = await execa('npm', ['install', '--save-exact', '-O', ...specs], { shell: true });
|
|
22
|
-
if (result.exitCode !== 0) {
|
|
23
|
-
console.error(result.stderr);
|
|
24
|
-
process.exit(result.exitCode);
|
|
25
|
-
}
|
|
26
|
-
if (!options.bundle) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const PREAMBLE = `// AUTOMATICALLY GENERATED FILE. DO NOT EDIT.
|
|
30
|
-
//
|
|
31
|
-
// This code is never executed but is detected by the static analysis of
|
|
32
|
-
// bundlers such as \`@vercel/ncc\`. The require() expression that selects
|
|
33
|
-
// the right binary module for the current platform is too dynamic to be
|
|
34
|
-
// analyzable by bundler analyses, so this module provides an exhaustive
|
|
35
|
-
// static list for those analyses.
|
|
36
|
-
|
|
37
|
-
return;
|
|
38
|
-
|
|
39
|
-
`;
|
|
40
|
-
const requires = targets.map(name => `require('${name}');`).join('\n');
|
|
41
|
-
await fs.writeFile(options.bundle, PREAMBLE + requires + '\n');
|
|
42
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
4
|
-
import * as temp from 'temp';
|
|
5
|
-
import commandLineArgs from 'command-line-args';
|
|
6
|
-
import { execa } from 'execa';
|
|
7
|
-
const mktemp = temp.track().mkdir;
|
|
8
|
-
const OPTIONS = [
|
|
9
|
-
{ name: 'file', alias: 'f', type: String, defaultValue: 'index.node' },
|
|
10
|
-
{ name: 'out-dir', alias: 'd', type: String, defaultValue: null }
|
|
11
|
-
];
|
|
12
|
-
const require = createRequire(import.meta.url);
|
|
13
|
-
const LLVM = require('../../data/llvm.json');
|
|
14
|
-
const NODE = require('../../data/node.json');
|
|
15
|
-
function lookup(target) {
|
|
16
|
-
const path = LLVM[target];
|
|
17
|
-
if (!path) {
|
|
18
|
-
throw new Error(`Rust target ${target} not supported`);
|
|
19
|
-
}
|
|
20
|
-
const [platform, arch, abi] = path;
|
|
21
|
-
return NODE[platform][arch][abi];
|
|
22
|
-
}
|
|
23
|
-
export default async function main(argv) {
|
|
24
|
-
const options = commandLineArgs(OPTIONS, { argv, stopAtFirstUnknown: true });
|
|
25
|
-
argv = options._unknown || [];
|
|
26
|
-
if (argv.length === 0) {
|
|
27
|
-
throw new Error("Expected <target>.");
|
|
28
|
-
}
|
|
29
|
-
if (argv.length > 1) {
|
|
30
|
-
throw new Error(`Unexpected argument \`${argv[1]}\`.`);
|
|
31
|
-
}
|
|
32
|
-
const target = argv[0];
|
|
33
|
-
const addon = options.file;
|
|
34
|
-
const outDir = options['out-dir'] || path.join(process.cwd(), 'dist');
|
|
35
|
-
await fs.mkdir(outDir, { recursive: true });
|
|
36
|
-
const manifest = JSON.parse(await fs.readFile('package.json', { encoding: 'utf8' }));
|
|
37
|
-
const version = manifest.version;
|
|
38
|
-
const targets = manifest.neon.targets;
|
|
39
|
-
const name = targets[target];
|
|
40
|
-
if (!name) {
|
|
41
|
-
throw new Error(`Rust target ${target} not found in package.json.`);
|
|
42
|
-
}
|
|
43
|
-
const targetInfo = lookup(target);
|
|
44
|
-
const description = `Prebuilt binary package for \`${manifest.name}\` on \`${targetInfo.node}\`.`;
|
|
45
|
-
let prebuildManifest = {
|
|
46
|
-
name,
|
|
47
|
-
description,
|
|
48
|
-
version,
|
|
49
|
-
os: [targetInfo.platform],
|
|
50
|
-
cpu: [targetInfo.arch],
|
|
51
|
-
main: "index.node",
|
|
52
|
-
files: ["README.md", "index.node"]
|
|
53
|
-
};
|
|
54
|
-
const OPTIONAL_KEYS = [
|
|
55
|
-
'author', 'repository', 'keywords', 'bugs', 'homepage', 'license', 'engines'
|
|
56
|
-
];
|
|
57
|
-
for (const key of OPTIONAL_KEYS) {
|
|
58
|
-
if (manifest[key]) {
|
|
59
|
-
prebuildManifest[key] = manifest[key];
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
const tmpdir = await mktemp('neon-');
|
|
63
|
-
await fs.writeFile(path.join(tmpdir, "package.json"), JSON.stringify(prebuildManifest, null, 2));
|
|
64
|
-
await fs.copyFile(addon, path.join(tmpdir, "index.node"));
|
|
65
|
-
await fs.writeFile(path.join(tmpdir, "README.md"), `# \`${name}\`\n\n${description}\n`);
|
|
66
|
-
const result = await execa("npm", ["pack", "--json"], {
|
|
67
|
-
shell: true,
|
|
68
|
-
cwd: tmpdir,
|
|
69
|
-
stdio: ['pipe', 'pipe', 'inherit']
|
|
70
|
-
});
|
|
71
|
-
if (result.exitCode !== 0) {
|
|
72
|
-
process.exit(result.exitCode);
|
|
73
|
-
}
|
|
74
|
-
// FIXME: comment linking to the npm issue this fixes
|
|
75
|
-
const tarball = JSON.parse(result.stdout)[0].filename.replace('@', '').replace('/', '-');
|
|
76
|
-
const dest = path.join(outDir, tarball);
|
|
77
|
-
// Copy instead of move since e.g. GitHub Actions Windows runners host temp directories
|
|
78
|
-
// on a different device (which causes fs.renameSync to fail).
|
|
79
|
-
await fs.copyFile(path.join(tmpdir, tarball), dest);
|
|
80
|
-
console.log(dest);
|
|
81
|
-
}
|
package/dist/usage.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
import commandLineUsage from 'command-line-usage';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
function pink(text) {
|
|
5
|
-
return chalk.bold.hex('#e75480')(text);
|
|
6
|
-
}
|
|
7
|
-
function blue(text) {
|
|
8
|
-
return chalk.bold.cyanBright(text);
|
|
9
|
-
}
|
|
10
|
-
function yellow(text) {
|
|
11
|
-
return chalk.bold.yellowBright(text);
|
|
12
|
-
}
|
|
13
|
-
function green(text) {
|
|
14
|
-
return chalk.bold.greenBright(text);
|
|
15
|
-
}
|
|
16
|
-
function commandUsage(name, command) {
|
|
17
|
-
const sections = [
|
|
18
|
-
{
|
|
19
|
-
content: `${pink('Neon:')} ${name} - ${COMMAND_SUMMARIES[name]}`,
|
|
20
|
-
raw: true
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
header: blue('Usage:'),
|
|
24
|
-
content: `${blue('$')} ${command.syntax}`
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
header: yellow('Options:'),
|
|
28
|
-
content: command.options
|
|
29
|
-
}
|
|
30
|
-
];
|
|
31
|
-
if (command.seeAlso) {
|
|
32
|
-
sections.push({
|
|
33
|
-
header: green('See Also:'),
|
|
34
|
-
content: command.seeAlso
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
return commandLineUsage(sections);
|
|
38
|
-
}
|
|
39
|
-
function mainUsage() {
|
|
40
|
-
const require = createRequire(import.meta.url);
|
|
41
|
-
const version = require('../package.json').version;
|
|
42
|
-
const sections = [
|
|
43
|
-
{
|
|
44
|
-
content: `${pink('Neon:')} CLI v${version}`,
|
|
45
|
-
raw: true
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
header: blue('Usage:'),
|
|
49
|
-
content: `${blue('$')} neon <command> <options>`
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
header: yellow('Commands:'),
|
|
53
|
-
content: [
|
|
54
|
-
{ name: 'help', summary: COMMAND_SUMMARIES['help'] + '.' },
|
|
55
|
-
{ name: 'dist', summary: COMMAND_SUMMARIES['dist'] + '.' },
|
|
56
|
-
{ name: 'cross-dist', summary: COMMAND_SUMMARIES['cross-dist'] + '.' },
|
|
57
|
-
{ name: 'cross-pack', summary: COMMAND_SUMMARIES['cross-pack'] + '.' },
|
|
58
|
-
{ name: 'cross-install', summary: COMMAND_SUMMARIES['cross-install'] + '.' }
|
|
59
|
-
]
|
|
60
|
-
}
|
|
61
|
-
];
|
|
62
|
-
return commandLineUsage(sections);
|
|
63
|
-
}
|
|
64
|
-
const COMMAND_SUMMARIES = {
|
|
65
|
-
'help': 'Display help information about Neon',
|
|
66
|
-
'dist': 'Generate a .node file from a build',
|
|
67
|
-
'cross-dist': 'Generate a .node file from a cross-compiled prebuild',
|
|
68
|
-
'cross-pack': 'Create an npm tarball from a cross-compiled prebuild',
|
|
69
|
-
'cross-install': 'Install dependencies on cross-compiled prebuilds'
|
|
70
|
-
};
|
|
71
|
-
const COMMAND_USAGES = {
|
|
72
|
-
'help': {
|
|
73
|
-
syntax: 'neon help <command>',
|
|
74
|
-
options: [
|
|
75
|
-
{ name: '<command>', summary: 'Command to display help information about.' }
|
|
76
|
-
]
|
|
77
|
-
},
|
|
78
|
-
'dist': {
|
|
79
|
-
syntax: 'neon dist [-n <name>] [-l <log>|-f <dylib>] [-o <dist>]',
|
|
80
|
-
options: [
|
|
81
|
-
{ name: '-n, --name', summary: 'Crate name. (Default: $npm_package_name)' },
|
|
82
|
-
{ name: '-l, --log <log>', summary: 'Find dylib path from cargo messages <log>. (Default: stdin)' },
|
|
83
|
-
{ name: '-f, --file <dylib>', summary: 'Build .node from dylib file <dylib>.' },
|
|
84
|
-
{ name: '-o, --out <dist>', summary: 'Copy output to file <dist>. (Default: index.node)' }
|
|
85
|
-
],
|
|
86
|
-
seeAlso: [
|
|
87
|
-
{ name: 'cargo messages', summary: '<https://doc.rust-lang.org/cargo/reference/external-tools.html>' }
|
|
88
|
-
]
|
|
89
|
-
},
|
|
90
|
-
'cross-dist': {
|
|
91
|
-
syntax: 'neon cross-dist [-n <name>] [-l <log>|-f <dylib>] [-d <dir>] [-o <dist>]',
|
|
92
|
-
options: [
|
|
93
|
-
{ name: '-n, --name', summary: 'Crate name. (Default: $npm_package_name)' },
|
|
94
|
-
{ name: '-l, --log <log>', summary: 'Find dylib path from cargo messages <log>. (Default: stdin)' },
|
|
95
|
-
{ name: '-d, --dir <dir>', summary: 'Crate workspace root directory. (Default: .)' },
|
|
96
|
-
{
|
|
97
|
-
name: '',
|
|
98
|
-
summary: 'This is needed to normalize paths from the log data, which cross-rs provides from within the mounted Docker filesystem, back to the host filesystem.'
|
|
99
|
-
},
|
|
100
|
-
{ name: '-f, --file <dylib>', summary: 'Build .node from dylib file <dylib>.' },
|
|
101
|
-
{ name: '-o, --out <dist>', summary: 'Copy output to file <dist>. (Default: index.node)' }
|
|
102
|
-
],
|
|
103
|
-
seeAlso: [
|
|
104
|
-
{ name: 'cargo messages', summary: '<https://doc.rust-lang.org/cargo/reference/external-tools.html>' },
|
|
105
|
-
{ name: 'cross-rs', summary: '<https://github.com/cross-rs/cross>' }
|
|
106
|
-
]
|
|
107
|
-
},
|
|
108
|
-
'cross-pack': {
|
|
109
|
-
syntax: 'neon cross-pack [-f <addon>] <target>',
|
|
110
|
-
options: [
|
|
111
|
-
{ name: '-f, --file <addon>', summary: 'Prebuilt .node file to pack. (Default: index.node)' },
|
|
112
|
-
{ name: '<target>', summary: 'Rust target triple the addon was built for.' }
|
|
113
|
-
],
|
|
114
|
-
seeAlso: [
|
|
115
|
-
{ name: 'Rust platform support', summary: '<https://doc.rust-lang.org/rustc/platform-support.html>' },
|
|
116
|
-
{ name: 'npm pack', summary: '<https://docs.npmjs.com/cli/commands/npm-pack>' },
|
|
117
|
-
{ name: 'cross-rs', summary: '<https://github.com/cross-rs/cross>' }
|
|
118
|
-
]
|
|
119
|
-
},
|
|
120
|
-
'cross-install': {
|
|
121
|
-
syntax: 'neon cross-install [-b <file>|-B]',
|
|
122
|
-
options: [
|
|
123
|
-
{ name: '-b, --bundle <file>', summary: 'File to generate bundling metadata. (Default: .targets)' },
|
|
124
|
-
{
|
|
125
|
-
name: '',
|
|
126
|
-
summary: 'This generated file ensures support for bundlers (e.g. @vercel/ncc), which rely on static analysis to detect and enable any addons used by the library.'
|
|
127
|
-
},
|
|
128
|
-
{ name: '-B, --no-bundle', summary: 'Do not generate bundling metadata.' }
|
|
129
|
-
],
|
|
130
|
-
seeAlso: [
|
|
131
|
-
{ name: 'ncc', summary: '<https://github.com/vercel/ncc>' }
|
|
132
|
-
]
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
export function printUsage(command) {
|
|
136
|
-
if (command) {
|
|
137
|
-
console.error(commandUsage(command, COMMAND_USAGES[command]));
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
console.error(mainUsage());
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
export function die(msg) {
|
|
144
|
-
printUsage();
|
|
145
|
-
if (msg) {
|
|
146
|
-
console.error();
|
|
147
|
-
console.error(msg);
|
|
148
|
-
}
|
|
149
|
-
process.exit(1);
|
|
150
|
-
}
|