libdragon 10.8.0 → 10.8.2

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 CHANGED
@@ -1,12 +1,65 @@
1
1
  # Docker Libdragon
2
2
 
3
- [![Build](https://github.com/anacierdem/libdragon-docker/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/anacierdem/libdragon-docker/actions/workflows/ci.yml)
3
+ [![Build](https://github.com/anacierdem/libdragon-docker/actions/workflows/release.yml/badge.svg?branch=master)](https://github.com/anacierdem/libdragon-docker/actions/workflows/release.yml)
4
4
 
5
- This is a wrapper for a docker container to make managing the libdragon toolchain easier.
5
+ This is a wrapper for a docker container to make managing the libdragon toolchain easier. It has the additional advantage that libdragon toolchain and library can be installed on a per-project basis instead of managing system-wide installations.
6
6
 
7
- ## Quick Install
7
+ ## Prerequisites
8
8
 
9
- On a system with [docker](https://www.docker.com/products/docker-desktop) (`>= 18`), grab a [pre-built executable](https://github.com/anacierdem/libdragon-docker/releases/latest) and put it somewhere on your PATH. Then navigate to the folder you want to initialize your project and invoke libdragon;
9
+ You should have [docker](https://www.docker.com/products/docker-desktop) (`>= 18`) installed on your system.
10
+
11
+ `git` is not strictly required to use the tool. Still, it is highly recommended to have git on your host machine as it will be used instead of the one in the container.
12
+
13
+ ## Installation
14
+
15
+ This is primarily a node.js script which is also packaged as an executable. You have two options:
16
+
17
+ ### pre-built
18
+
19
+ Download the [pre-built executable](https://github.com/anacierdem/libdragon-docker/releases/latest) for your system and put it somewhere on your PATH. It is discouraged to put it in your project folder.
20
+
21
+ <details>
22
+ <summary>Windows instructions</summary>
23
+
24
+ - Download the Windows executable and copy it to `C:\bin`
25
+ - Press `Windows + R` key combination and then enter `rundll32 sysdm.cpl,EditEnvironmentVariables`
26
+ - In the `Environment Variables` window find the `Path` variable under `User variables for <your user name>`
27
+ - Double click it and add a new entry as `C:\bin`
28
+ - Restart your computer.
29
+ - You should now be able to use the `libdragon` on a command line or PowerShell.
30
+ - To update it with a new version, just replace the file in `C:\bin`
31
+
32
+ </details>
33
+
34
+ <details>
35
+ <summary>MacOS instructions</summary>
36
+
37
+ - Download the MacOS executable and copy it to `/usr/local/bin`
38
+ - Right click it and choose `Open`.
39
+ - It will show a warning, approve it by clicking `Open` again. You can close the newly opened terminal window.
40
+ - You should now be able to use the `libdragon` command.
41
+ - To update it with a new version, replace the file in `/usr/local/bin` and repeat the other steps.
42
+
43
+ </details>
44
+
45
+ <details>
46
+ <summary>Linux instructions</summary>
47
+
48
+ - You should already know this :)
49
+
50
+ </details>
51
+
52
+ ### via NPM
53
+
54
+ Install [node.js](https://nodejs.org/en/download/) (`>= 14`) and install `libdragon` as a global NPM package;
55
+
56
+ npm install -g libdragon
57
+
58
+ To update the tool to the latest, do `npm i -g libdragon@latest`.
59
+
60
+ ## Quick Guide
61
+
62
+ Navigate to the folder you want to initialize your project and invoke libdragon;
10
63
 
11
64
  libdragon init
12
65
 
@@ -22,21 +75,60 @@ To update the library and rebuild/install all the artifacts;
22
75
 
23
76
  libdragon update
24
77
 
25
- `git` is not strictly required to use this tool as docker's git will be used instead. Still, it is highly recommended to have git on your host machine.
78
+ In general, you can invoke libdragon as follows;
26
79
 
27
- ## Overall usage
80
+ libdragon [flags] <action>
28
81
 
29
- This is primarily a node.js script which is also packaged as an executable. If you have [node.js](https://nodejs.org/en/download/) (`>= 14`), you can do a global install and use it as usual. To install `libdragon` as a global NPM package;
82
+ Run `libdragon help [action]` for more details on individual actions.
30
83
 
31
- npm install -g libdragon
84
+ ## Recipes
32
85
 
33
- To update the tool to the latest, do `npm i -g libdragon@latest`.
86
+ ### Using a different branch of libdragon
34
87
 
35
- You can invoke libdragon as follows;
88
+ Initialize your project as usual:
36
89
 
37
- libdragon [flags] <action>
90
+ libdragon init
38
91
 
39
- Run `libdragon help [action]` for more details on individual actions.
92
+ Then switch the submodule to the desired branch:
93
+
94
+ cd ./libdragon
95
+ git checkout opengl
96
+ cd ..
97
+ libdragon install
98
+
99
+ If your changes are on a different remote, then you will need to manage your git remotes as usual.
100
+
101
+ ### Testing changes on libdragon
102
+
103
+ As libdragon is an actively developed library, you may find yourself at a position where you want to change a few things on it and see how it works. In general, if you modify the files in `libdragon` folder of your project, you can install that version to the docker container by simply running:
104
+
105
+ libdragon install
106
+
107
+ This will update all the artifacts in your container and your new code will start linking against the new version when you re-build it via `libdragon make`. The build system should pick up the change in the library and re-compile the dependent files.
108
+
109
+ Instead of depending on the above command, you can re-build the library by making it a make dependency in your project:
110
+
111
+ ```makefile
112
+ libdragon-install: libdragon
113
+ $(MAKE) -C ./libdragon install
114
+
115
+ libdragon:
116
+ $(MAKE) -C ./libdragon
117
+ ```
118
+
119
+ If your build now depends on `libdragon-install`, it will force an install (which should be pretty quick if you don't have changes) and force the build system to rebuild your project when necessary.
120
+
121
+ If you clone this repository, this setup is pretty much ready for you. Make sure you have a working libdragon setup and you get the submodules (e.g `git submodule update --init`). Then you can run `libdragon make bench` to execute the code in `./src` with your library changes. Also see [test bench](#local-test-bench).
122
+
123
+ When managing your changes to the library, you have a few options:
124
+
125
+ #### **Using `submodule` vendor strategy.**
126
+
127
+ To be able to share your project with the library change, you would need to push it somewhere public and make sure it is cloned properly by your contributors. This is not recommended for keeping track of your changes but is very useful if you plan to contribute it back to upstream. In the latter case, you can push your submodule branch to your fork and easily open a PR.
128
+
129
+ #### **Using `subtree` vendor strategy.**
130
+
131
+ To be able to share your project with the library change, you just commit your changes. This is very useful for keeping track of your changes specific to your project. On the other hand this is not recommended if you plan to contribute it back to upstream because you will need to make sure your libdragon commits are isolated and do the juggling when pushing it somewhere via `git subtree push`.
40
132
 
41
133
  ## Working on this repository
42
134
 
@@ -86,15 +178,40 @@ This repository also uses [ed64](https://github.com/anacierdem/ed64), so you can
86
178
 
87
179
  There are also additional vscode launch configurations to build libdragon examples and tests based on the currently built and installed libdragon in the docker container. Most of these will always rebuild so that they will use the latest if you made and installed an alternative libdragon. The test bench itself and a few examples (that use the new build system) will already rebuild and reinstall libdragon automatically. These will always produce a rom image using the latest libdragon code in the active repository via its make dependencies. You can clean everything with the `clean` task (open the command palette and choose `Run Task -> clean`).
88
180
 
181
+ ### Developing the tool itself
182
+
183
+ For a quick development loop it really helps linking the code in this repository as the global libdragon installation. To do this run;
184
+
185
+ npm link
186
+
187
+ in the root of the repository. Once you do this, running `libdragon` will use the code here rather than the actual npm installation. Then you can test your changes in the libdragon project here or elsewhere on your computer.
188
+
189
+ When you are happy with your changes, you can verify you conform to the coding standards via:
190
+
191
+ npm run format-check
192
+ npm run lint-check
193
+
194
+ You can auto-fix applicable errors by running `format` and `lint` scripts instead. Additionally, typescript is used as the type system. To be able to get away with transpiling the code during development, jsDoc flavor of types are used instead of inline ones. To check your types, run:
195
+
196
+ npm run tsc
197
+
198
+ This repository uses [`semantic-release`](https://github.com/semantic-release/semantic-release) and manages releases from specially formatted commit messages. To simplify creating them you can use:
199
+
200
+ npx cz
201
+
202
+ It will create a `semantic-release` compatible commit from your current staged changes.
203
+
89
204
  ## As an NPM dependency
90
205
 
91
206
  You can install libdragon as an NPM dependency by `npm install libdragon --save` in order to use docker in your N64 projects. A `libdragon` command similar to global intallation is provided that can be used in your NPM scripts as follows;
92
207
 
93
- "scripts": {
94
- "prepare": "libdragon init"
95
- "build": "libdragon make",
96
- "clean": "libdragon make clean"
97
- }
208
+ ```json
209
+ "scripts": {
210
+ "prepare": "libdragon init"
211
+ "build": "libdragon make",
212
+ "clean": "libdragon make clean"
213
+ }
214
+ ```
98
215
 
99
216
  See [here](https://github.com/anacierdem/ed64-example) for a full example.
100
217
 
@@ -104,29 +221,27 @@ You can make an NPM package that a `libdragon` project can depend on. Just inclu
104
221
 
105
222
  For example this `package.json` in the dependent project;
106
223
 
107
- {
108
- "name": "libdragonDependentProject",
109
- "version": "1.0.0",
110
- "description": "...",
111
- "scripts": {
112
- "build": "libdragon make",
113
- "clean": "libdragon make clean",
114
- "prepare": "libdragon init"
115
- },
116
- "dependencies": {
117
- "libdragon": <version>,
118
- "ed64": <version>
119
- }
224
+ ```json
225
+ {
226
+ "name": "libdragonDependentProject",
227
+ "version": "1.0.0",
228
+ "description": "...",
229
+ "scripts": {
230
+ "build": "libdragon make",
231
+ "clean": "libdragon make clean",
232
+ "prepare": "libdragon init"
233
+ },
234
+ "dependencies": {
235
+ "libdragon": <version>,
236
+ "ed64": <version>
120
237
  }
238
+ }
239
+ ```
121
240
 
122
241
  will init the container for this project and run `make && make install` for `ed64` upon running `npm install`. To develop a dependency [this](https://github.com/anacierdem/libdragon-dependency) is a good starting point.
123
242
 
124
243
  This is an experimental dependency management.
125
244
 
126
- ## TODOS
127
-
128
- - [ ] Skip CI checks for irrelevant changes.
129
-
130
245
  ## Funding
131
246
 
132
247
  If this tool helped you, consider supporting its development by sponsoring it!
package/index.js CHANGED
@@ -18,9 +18,30 @@ const {
18
18
  const { parseParameters } = require('./modules/parameters');
19
19
  const { readProjectInfo, writeProjectInfo } = require('./modules/project-info');
20
20
 
21
+ // Note: it is not possible to merge these type definitions in a single comment
22
+ /**
23
+ * @template {any} [U=any]
24
+ * @typedef {(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never} UnionToIntersection
25
+ */
26
+ /**
27
+ * @template {any} [K=any]
28
+ * never does not break the call `info.options.CURRENT_ACTION.fn`
29
+ * @typedef {[K] extends [UnionToIntersection<K>] ? any : unknown} NoUnion
30
+ * @typedef {NoUnion<Exclude<Parameters<import('./modules/parameters').Actions[import('./modules/project-info').ActionsNoProject]['fn']>[0], undefined>>} EitherCLIOrLibdragonInfo
31
+ */
32
+
21
33
  parseParameters(process.argv)
22
34
  .then(readProjectInfo)
23
- .then((info) => info.options.CURRENT_ACTION.fn(info))
35
+ .then((info) => {
36
+ return info.options.CURRENT_ACTION.fn(
37
+ /** @type {EitherCLIOrLibdragonInfo} */ (info)
38
+ );
39
+ // This type make sure a similar restriction to this code block is enforced
40
+ // without adding unnecessary javascript.
41
+ // return isProjectAction(info)
42
+ // ? info.options.CURRENT_ACTION.fn(info)
43
+ // : info.options.CURRENT_ACTION.fn(info);
44
+ })
24
45
  .catch((e) => {
25
46
  if (e instanceof ParameterError) {
26
47
  log(chalk.red(e.message));
@@ -6,6 +6,9 @@ const { CONFIG_FILE, LIBDRAGON_PROJECT_MANIFEST } = require('../constants');
6
6
  const { fileExists, dirExists, log } = require('../helpers');
7
7
  const chalk = require('chalk');
8
8
 
9
+ /**
10
+ * @param {import('../project-info').LibdragonInfo} libdragonInfo
11
+ */
9
12
  const destroy = async (libdragonInfo) => {
10
13
  await destroyContainer(libdragonInfo);
11
14
 
@@ -22,13 +25,13 @@ const destroy = async (libdragonInfo) => {
22
25
  log(chalk.green('Done cleanup.'));
23
26
  };
24
27
 
25
- module.exports = {
28
+ module.exports = /** @type {const} */ ({
26
29
  name: 'destroy',
27
30
  fn: destroy,
28
- mustHaveProject: false,
31
+ forwardsRestParams: false,
29
32
  usage: {
30
33
  name: 'destroy',
31
34
  summary: 'Do clean-up for current project.',
32
35
  description: `Removes libdragon configuration from current project and removes any known containers but will not touch previously vendored files. \`libdragon\` will not work anymore for this project.`,
33
36
  },
34
- };
37
+ });
@@ -10,6 +10,11 @@ const {
10
10
  ParameterError,
11
11
  } = require('../helpers');
12
12
 
13
+ /**
14
+ * @param {string} stop
15
+ * @param {string} start
16
+ * @returns {Promise<string>}
17
+ */
13
18
  const findElf = async (stop, start = '.') => {
14
19
  start = path.resolve(start);
15
20
 
@@ -43,6 +48,9 @@ const findElf = async (stop, start = '.') => {
43
48
  }
44
49
  };
45
50
 
51
+ /**
52
+ * @param {import('../project-info').LibdragonInfo} info
53
+ */
46
54
  const disasm = async (info) => {
47
55
  let elfPath;
48
56
  if (info.options.FILE) {
@@ -89,7 +97,7 @@ const disasm = async (info) => {
89
97
  });
90
98
  };
91
99
 
92
- module.exports = {
100
+ module.exports = /** @type {const} */ ({
93
101
  name: 'disasm',
94
102
  fn: disasm,
95
103
  forwardsRestParams: true,
@@ -118,4 +126,4 @@ module.exports = {
118
126
  },
119
127
  ],
120
128
  },
121
- };
129
+ });
@@ -1,3 +1,7 @@
1
+ /**
2
+ *
3
+ * @param {import('../project-info').LibdragonInfo} libdragonInfo
4
+ */
1
5
  function dockerHostUserParams(libdragonInfo) {
2
6
  const { uid, gid } = libdragonInfo.userInfo;
3
7
  return ['-u', `${uid >= 0 ? uid : ''}:${gid >= 0 ? gid : ''}`];
@@ -8,12 +8,16 @@ const {
8
8
  toPosixPath,
9
9
  fileExists,
10
10
  dirExists,
11
+ CommandError,
11
12
  } = require('../helpers');
12
13
 
13
14
  const { start } = require('./start');
14
15
  const { dockerHostUserParams } = require('./docker-utils');
15
16
  const { installDependencies } = require('./utils');
16
17
 
18
+ /**
19
+ * @param {import('../project-info').LibdragonInfo} libdragonInfo
20
+ */
17
21
  function dockerRelativeWorkdir(libdragonInfo) {
18
22
  return (
19
23
  CONTAINER_TARGET_PATH +
@@ -22,10 +26,16 @@ function dockerRelativeWorkdir(libdragonInfo) {
22
26
  );
23
27
  }
24
28
 
29
+ /**
30
+ * @param {import('../project-info').LibdragonInfo} libdragonInfo
31
+ */
25
32
  function dockerRelativeWorkdirParams(libdragonInfo) {
26
33
  return ['--workdir', dockerRelativeWorkdir(libdragonInfo)];
27
34
  }
28
35
 
36
+ /**
37
+ * @param {import('../project-info').LibdragonInfo} info
38
+ */
29
39
  const exec = async (info) => {
30
40
  const parameters = info.options.EXTRA_PARAMS.slice(1);
31
41
  log(
@@ -37,21 +47,32 @@ const exec = async (info) => {
37
47
 
38
48
  const stdin = new PassThrough();
39
49
 
40
- const paramsWithConvertedPaths = parameters.map((item) => {
41
- if (item.startsWith('-')) {
50
+ /** @type {string[]} */
51
+ const paramsWithConvertedPaths = await Promise.all(
52
+ parameters.map(async (item) => {
53
+ if (item.startsWith('-')) {
54
+ return item;
55
+ }
56
+ if (
57
+ item.includes(path.sep) &&
58
+ ((await fileExists(item)) || (await dirExists(item)))
59
+ ) {
60
+ return toPosixPath(
61
+ path.isAbsolute(item) ? path.relative(process.cwd(), item) : item
62
+ );
63
+ }
42
64
  return item;
43
- }
44
- if (item.includes(path.sep) && (fileExists(item) || dirExists(item))) {
45
- return toPosixPath(
46
- path.isAbsolute(item) ? path.relative(process.cwd(), item) : item
47
- );
48
- }
49
- return item;
50
- });
65
+ })
66
+ );
51
67
 
68
+ /**
69
+ *
70
+ * @param {import('../project-info').LibdragonInfo} libdragonInfo
71
+ * @param {import('../helpers').SpawnOptions} opts
72
+ * @returns
73
+ */
52
74
  const tryCmd = (libdragonInfo, opts = {}) => {
53
75
  const enableTTY = Boolean(process.stdout.isTTY && process.stdin.isTTY);
54
-
55
76
  return (
56
77
  libdragonInfo.containerId &&
57
78
  dockerExec(
@@ -80,6 +101,9 @@ const exec = async (info) => {
80
101
  };
81
102
 
82
103
  let started = false;
104
+ /**
105
+ * @param {import('fs').ReadStream=} stdin
106
+ */
83
107
  const startOnceAndCmd = async (stdin) => {
84
108
  if (!started) {
85
109
  const newId = await start(info);
@@ -121,22 +145,30 @@ const exec = async (info) => {
121
145
  inheritStderr: false,
122
146
  // In the first run, pass the stdin to the process if it is not a TTY
123
147
  // o/w we loose a user input unnecesarily somehow.
124
- stdin: !process.stdin.isTTY && process.stdin,
148
+ stdin:
149
+ (!process.stdin.isTTY || undefined) &&
150
+ /** @type {import('fs').ReadStream} */ (
151
+ /** @type {unknown} */ (process.stdin)
152
+ ),
125
153
  });
126
154
  } catch (e) {
155
+ if (!(e instanceof CommandError)) {
156
+ throw e;
157
+ }
127
158
  if (
128
- !e.out ||
129
159
  // TODO: is there a better way?
130
160
  !e.out.toString().includes(info.containerId)
131
161
  ) {
132
162
  throw e;
133
163
  }
134
- await startOnceAndCmd(stdin);
164
+ await startOnceAndCmd(
165
+ /** @type {import('fs').ReadStream} */ (/** @type {unknown} */ (stdin))
166
+ );
135
167
  }
136
168
  return info;
137
169
  };
138
170
 
139
- module.exports = {
171
+ module.exports = /** @type {const} */ ({
140
172
  name: 'exec',
141
173
  fn: exec,
142
174
  forwardsRestParams: true,
@@ -152,4 +184,4 @@ module.exports = {
152
184
 
153
185
  Must be run in an initialized libdragon project.`,
154
186
  },
155
- };
187
+ });
@@ -3,7 +3,10 @@ const commandLineUsage = require('command-line-usage');
3
3
 
4
4
  const { print } = require('../helpers');
5
5
 
6
- const printUsage = (info) => {
6
+ /**
7
+ * @param {import('../project-info').CLIInfo} info
8
+ */
9
+ const printUsage = async (info) => {
7
10
  const actions = require('./');
8
11
  const globalOptionDefinitions = [
9
12
  {
@@ -48,11 +51,12 @@ const printUsage = (info) => {
48
51
  },
49
52
  ];
50
53
 
51
- const actionsToShow =
54
+ const actionsToShow = /** @type {(keyof actions)[]} */ (
52
55
  info?.options.EXTRA_PARAMS?.filter(
53
56
  (action) =>
54
57
  Object.keys(actions).includes(action) && !['help'].includes(action)
55
- ) ?? (info ? [info.options.CURRENT_ACTION.name] : []);
58
+ ) ?? (info ? [info.options.CURRENT_ACTION.name] : [])
59
+ );
56
60
 
57
61
  const sections = [
58
62
  {
@@ -96,13 +100,12 @@ const printUsage = (info) => {
96
100
  print(usage);
97
101
  };
98
102
 
99
- module.exports = {
103
+ module.exports = /** @type {const} */ ({
100
104
  name: 'help',
101
105
  fn: printUsage,
102
106
  forwardsRestParams: true,
103
- mustHaveProject: false,
104
107
  usage: {
105
108
  name: 'help [action]',
106
109
  summary: 'Display this help information or details for the given action.',
107
110
  },
108
- };
111
+ });
@@ -3,7 +3,6 @@ const path = require('path');
3
3
 
4
4
  const chalk = require('chalk').stderr;
5
5
 
6
- const { fn: install } = require('./install');
7
6
  const { start } = require('./start');
8
7
  const {
9
8
  installDependencies,
@@ -29,6 +28,10 @@ const {
29
28
  } = require('../helpers');
30
29
  const { syncImageAndStart } = require('./update-and-start');
31
30
 
31
+ /**
32
+ * @param {import('../project-info').LibdragonInfo} info
33
+ * @returns {Promise<"submodule" | "subtree" | undefined>}
34
+ */
32
35
  const autoDetect = async (info) => {
33
36
  const vendorTarget = path.relative(
34
37
  info.root,
@@ -60,15 +63,25 @@ const autoDetect = async (info) => {
60
63
  inheritStdin: false,
61
64
  inheritStdout: false,
62
65
  inheritStderr: false,
66
+ }).catch((e) => {
67
+ if (!(e instanceof CommandError)) {
68
+ throw e;
69
+ }
63
70
  });
64
71
 
65
- if (gitLogs.includes(`git-subtree-dir: ${info.vendorDirectory}`)) {
72
+ if (
73
+ gitLogs &&
74
+ gitLogs.includes(`git-subtree-dir: ${info.vendorDirectory}`)
75
+ ) {
66
76
  log(`${info.vendorDirectory} is a subtree.`);
67
77
  return 'subtree';
68
78
  }
69
79
  }
70
80
  };
71
81
 
82
+ /**
83
+ * @param {import('../project-info').LibdragonInfo} info
84
+ */
72
85
  const autoVendor = async (info) => {
73
86
  // Update the strategy information for the project if the flag is provided
74
87
  if (info.options.VENDOR_STRAT) {
@@ -96,6 +109,8 @@ const autoVendor = async (info) => {
96
109
  }
97
110
 
98
111
  await runGitMaybeHost(info, ['init']);
112
+
113
+ // TODO: TS thinks this is already defined
99
114
  const detectedStrategy = await autoDetect(info);
100
115
 
101
116
  if (
@@ -114,22 +129,32 @@ const autoVendor = async (info) => {
114
129
  );
115
130
  return {
116
131
  ...info,
117
- vendorStrategy: detectedStrategy,
132
+ vendorStrategy: /** @type {import('../parameters').VendorStrategy} */ (
133
+ detectedStrategy
134
+ ),
118
135
  };
119
136
  }
120
137
 
121
138
  if (info.vendorStrategy === 'submodule') {
122
- await runGitMaybeHost(info, [
123
- 'submodule',
124
- 'add',
125
- '--force',
126
- '--name',
127
- LIBDRAGON_SUBMODULE,
128
- '--branch',
129
- LIBDRAGON_BRANCH,
130
- LIBDRAGON_GIT,
131
- info.vendorDirectory,
132
- ]);
139
+ try {
140
+ await runGitMaybeHost(info, [
141
+ 'submodule',
142
+ 'add',
143
+ '--force',
144
+ '--name',
145
+ LIBDRAGON_SUBMODULE,
146
+ '--branch',
147
+ LIBDRAGON_BRANCH,
148
+ LIBDRAGON_GIT,
149
+ info.vendorDirectory,
150
+ ]);
151
+ } catch (e) {
152
+ if (!(e instanceof CommandError)) throw e;
153
+ // We speculate this is caused by the user, so replace it with a more useful error message.
154
+ e.message = `Unable to create submodule. If you have copied the executable in your project folder or you have a file named ${info.vendorDirectory}, removing it might solve this issue. Original error:\n${e.message}`;
155
+ throw e;
156
+ }
157
+
133
158
  return info;
134
159
  }
135
160
 
@@ -163,11 +188,14 @@ const autoVendor = async (info) => {
163
188
  ]);
164
189
  return info;
165
190
  }
191
+
192
+ return info;
166
193
  };
167
194
 
168
195
  /**
169
196
  * Initialize a new libdragon project in current working directory
170
197
  * Also downloads the image
198
+ * @param {import('../project-info').LibdragonInfo} info
171
199
  */
172
200
  async function init(info) {
173
201
  log(`Initializing a libdragon project at ${info.root}`);
@@ -176,7 +204,7 @@ async function init(info) {
176
204
  const manifestPath = path.join(info.root, LIBDRAGON_PROJECT_MANIFEST);
177
205
  const manifestStats = await fs.stat(manifestPath).catch((e) => {
178
206
  if (e.code !== 'ENOENT') throw e;
179
- return false;
207
+ return /** @type {const} */ (false);
180
208
  });
181
209
 
182
210
  if (manifestStats && !manifestStats.isDirectory()) {
@@ -216,7 +244,9 @@ async function init(info) {
216
244
  info.containerId = await start(info);
217
245
 
218
246
  // We have created a new container, save the new info ASAP
219
- await initGitAndCacheContainerId(info);
247
+ await initGitAndCacheContainerId(
248
+ /** @type Parameters<initGitAndCacheContainerId>[0] */ (info)
249
+ );
220
250
 
221
251
  info = await autoVendor(info);
222
252
 
@@ -233,10 +263,10 @@ async function init(info) {
233
263
  return info;
234
264
  }
235
265
 
236
- module.exports = {
266
+ module.exports = /** @type {const} */ ({
237
267
  name: 'init',
238
268
  fn: init,
239
- mustHaveProject: false,
269
+ forwardsRestParams: false,
240
270
  usage: {
241
271
  name: 'init',
242
272
  summary: 'Create a libdragon project in the current directory.',
@@ -249,4 +279,4 @@ module.exports = {
249
279
  If you have an existing project with an already vendored submodule or subtree libdragon copy, \`init\` will automatically detect it at the provided \`--directory\`.`,
250
280
  group: ['docker', 'vendoring'],
251
281
  },
252
- };
282
+ });