Skip to content

Commit dc25b68

Browse files
feat!: migrate from junobuild-didc to @icp-sdk/bindgen (#411)
* feat!: migrate from junobuild-didc to icp-sdk/bindgen * feat: review text * feat: review text * feat: silence output
1 parent 3eaa350 commit dc25b68

4 files changed

Lines changed: 55 additions & 49 deletions

File tree

src/constants/dev.constants.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ export const SATELLITE_PROJECT_NAME = 'satellite';
44
export const SPUTNIK_PROJECT_NAME = 'sputnik';
55

66
export const DEVELOPER_PROJECT_SRC_PATH = join(process.cwd(), 'src');
7-
export const DEVELOPER_PROJECT_SATELLITE_PATH = join(DEVELOPER_PROJECT_SRC_PATH, 'satellite');
7+
export const DEVELOPER_PROJECT_SATELLITE_PATH = join(
8+
DEVELOPER_PROJECT_SRC_PATH,
9+
SATELLITE_PROJECT_NAME
10+
);
811
export const DEVELOPER_PROJECT_SATELLITE_DECLARATIONS_PATH = join(
912
DEVELOPER_PROJECT_SRC_PATH,
1013
'declarations',
11-
'satellite'
14+
SATELLITE_PROJECT_NAME
1215
);
1316

1417
export const CARGO_TOML = 'Cargo.toml';
@@ -31,7 +34,7 @@ export const DEVELOPER_PROJECT_SATELLITE_INDEX_MJS = join(
3134
const TEMPLATE_PATH = '../templates/eject';
3235

3336
export const RUST_TEMPLATE_PATH = join(TEMPLATE_PATH, 'rust');
34-
export const RUST_TEMPLATE_SATELLITE_PATH = join(RUST_TEMPLATE_PATH, 'src', 'satellite');
37+
export const RUST_TEMPLATE_SATELLITE_PATH = join(RUST_TEMPLATE_PATH, 'src', SATELLITE_PROJECT_NAME);
3538

3639
export const TS_TEMPLATE_PATH = join(TEMPLATE_PATH, 'typescript');
3740
export const MJS_TEMPLATE_PATH = join(TEMPLATE_PATH, 'javascript');

src/services/functions/build/build.rust.services.ts

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {execute, formatBytes, gzipFile, spawn} from '@junobuild/cli-tools';
33
import {generateApi} from '@junobuild/did-tools';
44
import {red, yellow} from 'kleur';
55
import {existsSync} from 'node:fs';
6-
import {copyFile, lstat, mkdir, readFile, rename, writeFile} from 'node:fs/promises';
6+
import {copyFile, lstat, mkdir, readFile, rename, rm, writeFile} from 'node:fs/promises';
77
import {join, relative} from 'node:path';
88
import ora, {type Ora} from 'ora';
99
import {compare, minVersion, satisfies} from 'semver';
@@ -20,15 +20,16 @@ import {
2020
} from '../../../constants/dev.constants';
2121
import type {BuildArgs, BuildType} from '../../../types/build';
2222
import {
23+
checkBindgen,
2324
checkCandidExtractor,
2425
checkIcWasm,
25-
checkJunoDidc,
2626
checkWasi2ic
2727
} from '../../../utils/build.rust.utils';
2828
import {readSatelliteDid} from '../../../utils/did.utils';
2929
import {checkRustVersion} from '../../../utils/env.utils';
3030
import {formatTime} from '../../../utils/format.utils';
3131
import {readPackageJson} from '../../../utils/pkg.utils';
32+
import {detectPackageManager} from '../../../utils/pm.utils';
3233
import {readEmulatorConfigAndCreateDeployTargetDir} from '../../emulator/_fs.services';
3334
import {prepareJunoPkgForSatellite, prepareJunoPkgForSputnik} from './build.metadata.services';
3435
import {dispatchEmulatorTouchSatellite} from './touch.services';
@@ -55,9 +56,9 @@ export const buildRust = async ({
5556
return;
5657
}
5758

58-
const {valid: validDidc} = await checkJunoDidc();
59+
const {valid: validBindgen} = await checkBindgen();
5960

60-
if (!validDidc) {
61+
if (!validBindgen) {
6162
return;
6263
}
6364

@@ -216,40 +217,36 @@ const didc = async () => {
216217
return;
217218
}
218219

219-
const generate = async (type: 'js' | 'ts') => {
220-
const output = satellitedIdl(type);
220+
const pm = detectPackageManager();
221221

222-
await spawn({
223-
command: 'junobuild-didc',
224-
args: ['-i', SATELLITE_CUSTOM_DID_FILE, '-t', type, '-o', output]
225-
});
226-
227-
const content = await readFile(output, 'utf-8');
228-
229-
// Depending on the `tsconfig`, the `factory.did.js` file might be validated.
230-
// Cleaning the file prevents errors such as:
231-
// TS7031: Binding element 'IDL' implicitly has an 'any' type.
232-
const cleanJs = (content: string): string => {
233-
const cleanFactory = content.replace(
234-
/export const idlFactory = \({ IDL }\) => {/g,
235-
`// @ts-expect-error
236-
export const idlFactory = ({ IDL }) => {`
237-
);
238-
return cleanFactory.replace(
239-
/export const init = \({ IDL }\) => {/g,
240-
`// @ts-expect-error
241-
export const init = ({ IDL }) => {`
242-
);
243-
};
222+
const command = pm === 'npm' || isNullish(pm) ? 'npx' : pm;
244223

245-
const cleanedContent = type === 'js' ? cleanJs(content) : content;
224+
// --actor-disabled: skip generating actor files, since we handle those ourselves
225+
// --force: overwrite files. Required; otherwise, icp-bindgen would delete files at preprocess,
226+
// which causes issues when multiple .did files are located in the same folder.
227+
await spawn({
228+
command,
229+
args: [
230+
'icp-bindgen',
231+
'--did-file',
232+
SATELLITE_CUSTOM_DID_FILE,
233+
'--out-dir',
234+
DEVELOPER_PROJECT_SATELLITE_DECLARATIONS_PATH,
235+
'--actor-disabled',
236+
'--force'
237+
],
238+
silentOut: true
239+
});
246240

247-
await writeFile(output, `${AUTO_GENERATED}\n\n${cleanedContent}`);
248-
};
241+
// icp-bindgen generates the files in a `declarations` subfolder
242+
// using a different suffix for JavaScript as the one we used to use.
243+
// That's why we have to post-process the results.
244+
const generatedFolder = join(DEVELOPER_PROJECT_SATELLITE_DECLARATIONS_PATH, 'declarations');
249245

250-
const promises = (['js', 'ts'] as Array<'js' | 'ts'>).map(generate);
246+
await rename(join(generatedFolder, `${EXTENSION_DID_FILE_NAME}.d.ts`), satellitedIdl('ts'));
247+
await rename(join(generatedFolder, `${EXTENSION_DID_FILE_NAME}.js`), satellitedIdl('js'));
251248

252-
await Promise.all(promises);
249+
await rm(generatedFolder, {recursive: true, force: true});
253250
};
254251

255252
const api = async () => {

src/utils/build.rust.utils.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import {isNullish} from '@dfinity/utils';
12
import {execute} from '@junobuild/cli-tools';
23
import {magenta, yellow} from 'kleur';
34
import {IC_WASM_MIN_VERSION} from '../constants/dev.constants';
4-
import {checkCargoBinInstalled, checkIcWasmVersion} from './env.utils';
5+
import {checkIcWasmVersion, checkToolInstalled} from './env.utils';
6+
import {detectPackageManager} from './pm.utils';
57
import {confirmAndExit} from './prompt.utils';
68

79
export const checkIcWasm = async (): Promise<{valid: boolean}> => {
@@ -28,7 +30,7 @@ export const checkIcWasm = async (): Promise<{valid: boolean}> => {
2830
};
2931

3032
export const checkCandidExtractor = async (): Promise<{valid: boolean}> => {
31-
const {valid} = await checkCargoBinInstalled({
33+
const {valid} = await checkToolInstalled({
3234
command: 'candid-extractor',
3335
args: ['--version']
3436
});
@@ -53,10 +55,14 @@ export const checkCandidExtractor = async (): Promise<{valid: boolean}> => {
5355
return {valid: true};
5456
};
5557

56-
export const checkJunoDidc = async (): Promise<{valid: boolean}> => {
57-
const {valid} = await checkCargoBinInstalled({
58-
command: 'junobuild-didc',
59-
args: ['--version']
58+
export const checkBindgen = async (): Promise<{valid: boolean}> => {
59+
const pm = detectPackageManager();
60+
61+
const command = pm === 'npm' || isNullish(pm) ? 'npx' : pm;
62+
63+
const {valid} = await checkToolInstalled({
64+
command,
65+
args: ['icp-bindgen', '--version']
6066
});
6167

6268
if (valid === false) {
@@ -65,22 +71,22 @@ export const checkJunoDidc = async (): Promise<{valid: boolean}> => {
6571

6672
if (valid === 'error') {
6773
await confirmAndExit(
68-
`It seems that ${magenta(
69-
'junobuild-didc'
70-
)} is not installed. This is a useful tool for generating automatically JavaScript or TypeScript bindings. Would you like to install it?`
74+
`${magenta(
75+
'@icp-sdk/bindgen'
76+
)} is not available. This tool is required to generate API bindings. Would you like to install it now?`
7177
);
7278

7379
await execute({
74-
command: 'cargo',
75-
args: ['install', `junobuild-didc`]
80+
command: pm ?? 'npm',
81+
args: [pm === 'npm' ? 'i' : 'add', '@icp-sdk/bindgen', '-D']
7682
});
7783
}
7884

7985
return {valid: true};
8086
};
8187

8288
export const checkWasi2ic = async (): Promise<{valid: boolean}> => {
83-
const {valid} = await checkCargoBinInstalled({
89+
const {valid} = await checkToolInstalled({
8490
command: 'wasi2ic',
8591
args: ['--version']
8692
});

src/utils/env.utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const checkIcWasmVersion = async (): Promise<{valid: boolean | 'error'}>
7777
return {valid: true};
7878
};
7979

80-
export const checkCargoBinInstalled = async ({
80+
export const checkToolInstalled = async ({
8181
command,
8282
args
8383
}: {

0 commit comments

Comments
 (0)