@aztec/noir-noir_codegen 0.75.0-commit.8a71f57856e217a77b6e50cbc8833c1cd5395b96
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 +4 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +8 -0
- package/lib/main.d.ts +2 -0
- package/lib/main.js +37 -0
- package/lib/parseArgs.d.ts +8 -0
- package/lib/parseArgs.js +55 -0
- package/lib/utils/abi_type_with_generics.d.ts +70 -0
- package/lib/utils/abi_type_with_generics.js +88 -0
- package/lib/utils/demonomorphizer.d.ts +46 -0
- package/lib/utils/demonomorphizer.js +223 -0
- package/lib/utils/glob.d.ts +1 -0
- package/lib/utils/glob.js +5 -0
- package/lib/utils/typings_generator.d.ts +36 -0
- package/lib/utils/typings_generator.js +254 -0
- package/package.json +63 -0
package/README.md
ADDED
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TypingsGenerator } from './utils/typings_generator.js';
|
|
2
|
+
export const codegen = (programs, embedArtifact, useFixedLengthArrays) => {
|
|
3
|
+
return new TypingsGenerator(programs.map((program) => ({
|
|
4
|
+
circuitName: program[0],
|
|
5
|
+
artifact: embedArtifact ? program[1] : undefined,
|
|
6
|
+
abi: structuredClone(program[1].abi), // We'll mutate the ABI types when doing typescript codegen, so we clone it to avoid mutating the artifact.
|
|
7
|
+
})), useFixedLengthArrays).codegen();
|
|
8
|
+
};
|
package/lib/main.d.ts
ADDED
package/lib/main.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { parseArgs } from './parseArgs.js';
|
|
5
|
+
import { glob } from './utils/glob.js';
|
|
6
|
+
import { codegen } from './index.js';
|
|
7
|
+
function main() {
|
|
8
|
+
const cliConfig = parseArgs();
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
const files = getFilesToProcess(cwd, cliConfig.files);
|
|
11
|
+
if (files.length === 0) {
|
|
12
|
+
throw new Error('No files passed.' + '\n' + `\`${cliConfig.files}\` didn't match any input files in ${cwd}`);
|
|
13
|
+
}
|
|
14
|
+
const programs = files.map((file_path) => {
|
|
15
|
+
const program_name = path.parse(file_path).name;
|
|
16
|
+
const file_contents = fs.readFileSync(file_path).toString();
|
|
17
|
+
const { abi, bytecode } = JSON.parse(file_contents);
|
|
18
|
+
return [program_name, { abi, bytecode }];
|
|
19
|
+
});
|
|
20
|
+
const result = codegen(programs, !cliConfig.externalArtifact, cliConfig.useFixedLengthArrays);
|
|
21
|
+
const outputDir = path.resolve(cliConfig.outDir ?? './codegen');
|
|
22
|
+
const outputFile = path.join(outputDir, 'index.ts');
|
|
23
|
+
if (!fs.existsSync(outputDir))
|
|
24
|
+
fs.mkdirSync(outputDir);
|
|
25
|
+
fs.writeFileSync(outputFile, result);
|
|
26
|
+
}
|
|
27
|
+
function getFilesToProcess(cwd, filesOrPattern) {
|
|
28
|
+
let res = glob(cwd, filesOrPattern);
|
|
29
|
+
if (res.length === 0) {
|
|
30
|
+
// If there are no files found, but first parameter is surrounded with single quotes, we try again without quotes
|
|
31
|
+
const match = filesOrPattern[0].match(/'([\s\S]*)'/)?.[1];
|
|
32
|
+
if (match)
|
|
33
|
+
res = glob(cwd, [match]);
|
|
34
|
+
}
|
|
35
|
+
return res;
|
|
36
|
+
}
|
|
37
|
+
main();
|
package/lib/parseArgs.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { parse as commandLineArgs } from 'ts-command-line-args';
|
|
2
|
+
const DEFAULT_GLOB_PATTERN = './target/**/*.json';
|
|
3
|
+
export function parseArgs() {
|
|
4
|
+
const rawOptions = commandLineArgs({
|
|
5
|
+
glob: {
|
|
6
|
+
type: String,
|
|
7
|
+
defaultOption: true,
|
|
8
|
+
multiple: true,
|
|
9
|
+
defaultValue: [DEFAULT_GLOB_PATTERN],
|
|
10
|
+
description: 'Pattern that will be used to find program artifacts. Remember about adding quotes: noir-codegen "**/*.json".',
|
|
11
|
+
},
|
|
12
|
+
'out-dir': { type: String, optional: true, description: 'Output directory for generated files.' },
|
|
13
|
+
'input-dir': {
|
|
14
|
+
type: String,
|
|
15
|
+
optional: true,
|
|
16
|
+
description: 'Directory containing program artifact files. Inferred as lowest common path of all files if not specified.',
|
|
17
|
+
},
|
|
18
|
+
help: { type: Boolean, defaultValue: false, alias: 'h', description: 'Prints this message.' },
|
|
19
|
+
'external-artifact': {
|
|
20
|
+
type: Boolean,
|
|
21
|
+
defaultValue: false,
|
|
22
|
+
description: 'Does not embed the circuit artifact in the code, instead requiring passing the circuit artifact as an argument to the generated functions.',
|
|
23
|
+
},
|
|
24
|
+
'fixed-length-arrays': {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
defaultValue: false,
|
|
27
|
+
description: 'Use fixed-length arrays for inputs and outputs.',
|
|
28
|
+
},
|
|
29
|
+
}, {
|
|
30
|
+
helpArg: 'help',
|
|
31
|
+
headerContentSections: [
|
|
32
|
+
{
|
|
33
|
+
content: `\
|
|
34
|
+
noir-codegen generates TypeScript wrappers for Noir programs to simplify replicating your Noir logic in JS.`,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
footerContentSections: [
|
|
38
|
+
{
|
|
39
|
+
header: 'Example Usage',
|
|
40
|
+
content: `\
|
|
41
|
+
noir-codegen --out-dir app/noir_programs './target/*.json'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
You can read more about noir-codegen at {underline https://github.com/noir-lang/noir}.`,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
files: rawOptions.glob,
|
|
50
|
+
outDir: rawOptions['out-dir'],
|
|
51
|
+
inputDir: rawOptions['input-dir'],
|
|
52
|
+
externalArtifact: rawOptions['external-artifact'],
|
|
53
|
+
useFixedLengthArrays: rawOptions['fixed-length-arrays'],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { AbiType } from '@noir-lang/noirc_abi';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a binding to a generic.
|
|
4
|
+
*/
|
|
5
|
+
export declare class BindingId {
|
|
6
|
+
id: number;
|
|
7
|
+
isNumeric: boolean;
|
|
8
|
+
constructor(id: number, isNumeric: boolean);
|
|
9
|
+
}
|
|
10
|
+
export type StructType = {
|
|
11
|
+
path: string;
|
|
12
|
+
fields: {
|
|
13
|
+
name: string;
|
|
14
|
+
type: AbiTypeWithGenerics;
|
|
15
|
+
}[];
|
|
16
|
+
/** The generics of the struct, bound to the fields */
|
|
17
|
+
generics: BindingId[];
|
|
18
|
+
};
|
|
19
|
+
export type StringType = {
|
|
20
|
+
kind: 'string';
|
|
21
|
+
length: number | BindingId | null;
|
|
22
|
+
};
|
|
23
|
+
export type Constant = {
|
|
24
|
+
kind: 'constant';
|
|
25
|
+
value: number;
|
|
26
|
+
};
|
|
27
|
+
export type ArrayType = {
|
|
28
|
+
kind: 'array';
|
|
29
|
+
length: number | BindingId | null;
|
|
30
|
+
type: AbiTypeWithGenerics;
|
|
31
|
+
};
|
|
32
|
+
export type Tuple = {
|
|
33
|
+
kind: 'tuple';
|
|
34
|
+
fields: AbiTypeWithGenerics[];
|
|
35
|
+
};
|
|
36
|
+
export type Struct = {
|
|
37
|
+
kind: 'struct';
|
|
38
|
+
structType: StructType;
|
|
39
|
+
/** The arguments are the concrete instantiation of the generics in the struct type. */
|
|
40
|
+
args: AbiTypeWithGenerics[];
|
|
41
|
+
};
|
|
42
|
+
export type AbiTypeWithGenerics = {
|
|
43
|
+
kind: 'field';
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'boolean';
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'integer';
|
|
48
|
+
sign: string;
|
|
49
|
+
width: number;
|
|
50
|
+
} | {
|
|
51
|
+
kind: 'binding';
|
|
52
|
+
id: BindingId;
|
|
53
|
+
} | {
|
|
54
|
+
kind: 'constant';
|
|
55
|
+
value: number;
|
|
56
|
+
} | StringType | ArrayType | Tuple | Struct;
|
|
57
|
+
/**
|
|
58
|
+
* Maps an ABI type to an ABI type with generics.
|
|
59
|
+
* This performs pure type conversion, and does not generate any bindings.
|
|
60
|
+
*/
|
|
61
|
+
export declare function mapAbiTypeToAbiTypeWithGenerics(abiType: AbiType): AbiTypeWithGenerics;
|
|
62
|
+
/**
|
|
63
|
+
* Finds the structs in an ABI type.
|
|
64
|
+
* This won't explore nested structs.
|
|
65
|
+
*/
|
|
66
|
+
export declare function findStructsInType(abiType: AbiTypeWithGenerics): Struct[];
|
|
67
|
+
/**
|
|
68
|
+
* Finds all the structs in an ABI type, including nested structs.
|
|
69
|
+
*/
|
|
70
|
+
export declare function findAllStructsInType(abiType: AbiTypeWithGenerics): Struct[];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a binding to a generic.
|
|
3
|
+
*/
|
|
4
|
+
export class BindingId {
|
|
5
|
+
id;
|
|
6
|
+
isNumeric;
|
|
7
|
+
constructor(id, isNumeric) {
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.isNumeric = isNumeric;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Maps an ABI type to an ABI type with generics.
|
|
14
|
+
* This performs pure type conversion, and does not generate any bindings.
|
|
15
|
+
*/
|
|
16
|
+
export function mapAbiTypeToAbiTypeWithGenerics(abiType) {
|
|
17
|
+
switch (abiType.kind) {
|
|
18
|
+
case 'field':
|
|
19
|
+
case 'boolean':
|
|
20
|
+
case 'string':
|
|
21
|
+
case 'integer':
|
|
22
|
+
return abiType;
|
|
23
|
+
case 'array':
|
|
24
|
+
return {
|
|
25
|
+
kind: 'array',
|
|
26
|
+
length: abiType.length,
|
|
27
|
+
type: mapAbiTypeToAbiTypeWithGenerics(abiType.type),
|
|
28
|
+
};
|
|
29
|
+
case 'struct': {
|
|
30
|
+
const structType = {
|
|
31
|
+
path: abiType.path,
|
|
32
|
+
fields: abiType.fields.map((field) => ({
|
|
33
|
+
name: field.name,
|
|
34
|
+
type: mapAbiTypeToAbiTypeWithGenerics(field.type),
|
|
35
|
+
})),
|
|
36
|
+
generics: [],
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
kind: 'struct',
|
|
40
|
+
structType,
|
|
41
|
+
args: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
case 'tuple':
|
|
45
|
+
return {
|
|
46
|
+
kind: 'tuple',
|
|
47
|
+
fields: abiType.fields.map(mapAbiTypeToAbiTypeWithGenerics),
|
|
48
|
+
};
|
|
49
|
+
default: {
|
|
50
|
+
const exhaustiveCheck = abiType;
|
|
51
|
+
throw new Error(`Unhandled abi type: ${exhaustiveCheck}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Finds the structs in an ABI type.
|
|
57
|
+
* This won't explore nested structs.
|
|
58
|
+
*/
|
|
59
|
+
export function findStructsInType(abiType) {
|
|
60
|
+
switch (abiType.kind) {
|
|
61
|
+
case 'field':
|
|
62
|
+
case 'boolean':
|
|
63
|
+
case 'string':
|
|
64
|
+
case 'integer':
|
|
65
|
+
return [];
|
|
66
|
+
case 'array':
|
|
67
|
+
return findStructsInType(abiType.type);
|
|
68
|
+
case 'tuple':
|
|
69
|
+
return abiType.fields.flatMap(findStructsInType);
|
|
70
|
+
case 'struct':
|
|
71
|
+
return [abiType];
|
|
72
|
+
default: {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Finds all the structs in an ABI type, including nested structs.
|
|
79
|
+
*/
|
|
80
|
+
export function findAllStructsInType(abiType) {
|
|
81
|
+
let allStructs = [];
|
|
82
|
+
let lastStructs = findStructsInType(abiType);
|
|
83
|
+
while (lastStructs.length > 0) {
|
|
84
|
+
allStructs = allStructs.concat(lastStructs);
|
|
85
|
+
lastStructs = lastStructs.flatMap((struct) => struct.structType.fields.flatMap((field) => findStructsInType(field.type)));
|
|
86
|
+
}
|
|
87
|
+
return allStructs;
|
|
88
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type AbiTypeWithGenerics, type StructType } from './abi_type_with_generics.js';
|
|
2
|
+
export interface DemonomorphizerConfig {
|
|
3
|
+
leaveArrayLengthsUnbounded: boolean;
|
|
4
|
+
leaveStringLengthsUnbounded: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Demonomorphizes a list of ABI types adding generics to structs.
|
|
8
|
+
* Since monomorphization of the generics destroys information, this process is not guaranteed to return the original structure.
|
|
9
|
+
* However, it should successfully unify all struct types that share the same name and field names.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Demonomorphizer {
|
|
12
|
+
private types;
|
|
13
|
+
private config;
|
|
14
|
+
private variantsMap;
|
|
15
|
+
private visitedStructs;
|
|
16
|
+
private lastBindingId;
|
|
17
|
+
/**
|
|
18
|
+
* Demonomorphizes the passed in ABI types, mutating them.
|
|
19
|
+
*/
|
|
20
|
+
static demonomorphize(abiTypes: AbiTypeWithGenerics[], config: DemonomorphizerConfig): void;
|
|
21
|
+
private constructor();
|
|
22
|
+
/**
|
|
23
|
+
* Finds all the variants of the structs in the types.
|
|
24
|
+
* A variant is every use of a struct with the same name and fields.
|
|
25
|
+
*/
|
|
26
|
+
private fillVariantsMap;
|
|
27
|
+
private demonomorphizeStructs;
|
|
28
|
+
/**
|
|
29
|
+
* Demonomorphizes a struct, by demonomorphizing its dependencies first.
|
|
30
|
+
* Then it'll unify the types of the variants generating a unique generic type.
|
|
31
|
+
* It'll also generate args that instantiate the generic type with the concrete arguments for each variant.
|
|
32
|
+
*/
|
|
33
|
+
private demonomorphizeStruct;
|
|
34
|
+
/**
|
|
35
|
+
* Tries to unify the types of a set of variants recursively.
|
|
36
|
+
* Unification will imply replacing some properties with bindings and pushing bindings to the generics of the struct.
|
|
37
|
+
*/
|
|
38
|
+
private unifyTypes;
|
|
39
|
+
/**
|
|
40
|
+
* We consider a struct to be the same if it has the same name and field names.
|
|
41
|
+
* Structs with the same id will be unified into a single type by the demonomorphizer.
|
|
42
|
+
*/
|
|
43
|
+
static buildIdForStruct(struct: StructType): string;
|
|
44
|
+
private buildBindingAndPushToVariants;
|
|
45
|
+
private buildNumericBindingAndPushToVariants;
|
|
46
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { BindingId, findAllStructsInType, findStructsInType, } from './abi_type_with_generics.js';
|
|
2
|
+
/**
|
|
3
|
+
* Demonomorphizes a list of ABI types adding generics to structs.
|
|
4
|
+
* Since monomorphization of the generics destroys information, this process is not guaranteed to return the original structure.
|
|
5
|
+
* However, it should successfully unify all struct types that share the same name and field names.
|
|
6
|
+
*/
|
|
7
|
+
export class Demonomorphizer {
|
|
8
|
+
types;
|
|
9
|
+
config;
|
|
10
|
+
variantsMap;
|
|
11
|
+
visitedStructs;
|
|
12
|
+
lastBindingId = 0;
|
|
13
|
+
/**
|
|
14
|
+
* Demonomorphizes the passed in ABI types, mutating them.
|
|
15
|
+
*/
|
|
16
|
+
static demonomorphize(abiTypes, config) {
|
|
17
|
+
new Demonomorphizer(abiTypes, config);
|
|
18
|
+
}
|
|
19
|
+
constructor(types, config) {
|
|
20
|
+
this.types = types;
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.variantsMap = new Map();
|
|
23
|
+
this.fillVariantsMap();
|
|
24
|
+
this.visitedStructs = new Map();
|
|
25
|
+
this.demonomorphizeStructs();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Finds all the variants of the structs in the types.
|
|
29
|
+
* A variant is every use of a struct with the same name and fields.
|
|
30
|
+
*/
|
|
31
|
+
fillVariantsMap() {
|
|
32
|
+
const allStructs = this.types.flatMap(findAllStructsInType);
|
|
33
|
+
for (const struct of allStructs) {
|
|
34
|
+
const id = Demonomorphizer.buildIdForStruct(struct.structType);
|
|
35
|
+
const variants = this.variantsMap.get(id) ?? [];
|
|
36
|
+
variants.push(struct);
|
|
37
|
+
this.variantsMap.set(id, variants);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
demonomorphizeStructs() {
|
|
41
|
+
for (const type of this.types) {
|
|
42
|
+
const topLevelStructs = findStructsInType(type);
|
|
43
|
+
for (const struct of topLevelStructs) {
|
|
44
|
+
this.demonomorphizeStruct(struct);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Demonomorphizes a struct, by demonomorphizing its dependencies first.
|
|
50
|
+
* Then it'll unify the types of the variants generating a unique generic type.
|
|
51
|
+
* It'll also generate args that instantiate the generic type with the concrete arguments for each variant.
|
|
52
|
+
*/
|
|
53
|
+
demonomorphizeStruct(struct) {
|
|
54
|
+
const id = Demonomorphizer.buildIdForStruct(struct.structType);
|
|
55
|
+
if (this.visitedStructs.has(id)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const dependencies = struct.structType.fields.flatMap((field) => findStructsInType(field.type));
|
|
59
|
+
for (const dependency of dependencies) {
|
|
60
|
+
this.demonomorphizeStruct(dependency);
|
|
61
|
+
}
|
|
62
|
+
if (this.visitedStructs.has(id)) {
|
|
63
|
+
throw new Error('Circular dependency detected');
|
|
64
|
+
}
|
|
65
|
+
const variants = this.variantsMap.get(id);
|
|
66
|
+
const mappedStructType = struct.structType;
|
|
67
|
+
for (let i = 0; i < struct.structType.fields.length; i++) {
|
|
68
|
+
const variantTypes = variants.map((variant) => variant.structType.fields[i].type);
|
|
69
|
+
const mappedType = this.unifyTypes(variantTypes, mappedStructType.generics, variants);
|
|
70
|
+
mappedStructType.fields[i].type = mappedType;
|
|
71
|
+
}
|
|
72
|
+
// Mutate variants setting the new struct type
|
|
73
|
+
variants.forEach((variant) => (variant.structType = mappedStructType));
|
|
74
|
+
this.visitedStructs.set(id, mappedStructType);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Tries to unify the types of a set of variants recursively.
|
|
78
|
+
* Unification will imply replacing some properties with bindings and pushing bindings to the generics of the struct.
|
|
79
|
+
*/
|
|
80
|
+
unifyTypes(types, generics, // Mutates generics adding new bindings
|
|
81
|
+
variants) {
|
|
82
|
+
const kinds = new Set(types.map((type) => type.kind));
|
|
83
|
+
if (kinds.size > 1) {
|
|
84
|
+
return this.buildBindingAndPushToVariants(types, generics, variants);
|
|
85
|
+
}
|
|
86
|
+
switch (types[0].kind) {
|
|
87
|
+
case 'field':
|
|
88
|
+
case 'boolean':
|
|
89
|
+
case 'binding':
|
|
90
|
+
return types[0];
|
|
91
|
+
case 'integer': {
|
|
92
|
+
if (allDeepEqual(types)) {
|
|
93
|
+
return types[0];
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
return this.buildBindingAndPushToVariants(types, generics, variants);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
case 'string': {
|
|
100
|
+
const strings = types;
|
|
101
|
+
const unifiedStringType = strings[0];
|
|
102
|
+
if (strings.every((string) => string.length === unifiedStringType.length)) {
|
|
103
|
+
return unifiedStringType;
|
|
104
|
+
}
|
|
105
|
+
else if (!this.config.leaveStringLengthsUnbounded) {
|
|
106
|
+
unifiedStringType.length = this.buildNumericBindingAndPushToVariants(strings.map((string) => {
|
|
107
|
+
if (typeof string.length !== 'number') {
|
|
108
|
+
throw new Error('Trying to unify strings with bindings');
|
|
109
|
+
}
|
|
110
|
+
return string.length;
|
|
111
|
+
}), generics, variants);
|
|
112
|
+
return unifiedStringType;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
unifiedStringType.length = null;
|
|
116
|
+
return unifiedStringType;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
case 'array': {
|
|
120
|
+
const arrays = types;
|
|
121
|
+
const unifiedArrayType = arrays[0];
|
|
122
|
+
if (!arrays.every((array) => array.length === unifiedArrayType.length)) {
|
|
123
|
+
if (!this.config.leaveArrayLengthsUnbounded) {
|
|
124
|
+
unifiedArrayType.length = this.buildNumericBindingAndPushToVariants(arrays.map((array) => {
|
|
125
|
+
if (typeof array.length !== 'number') {
|
|
126
|
+
throw new Error('Trying to unify arrays with bindings');
|
|
127
|
+
}
|
|
128
|
+
return array.length;
|
|
129
|
+
}), generics, variants);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
unifiedArrayType.length = null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
unifiedArrayType.type = this.unifyTypes(arrays.map((array) => array.type), generics, variants);
|
|
136
|
+
return unifiedArrayType;
|
|
137
|
+
}
|
|
138
|
+
case 'tuple': {
|
|
139
|
+
const tuples = types;
|
|
140
|
+
const unifiedTupleType = tuples[0];
|
|
141
|
+
for (let i = 0; i < unifiedTupleType.fields.length; i++) {
|
|
142
|
+
unifiedTupleType.fields[i] = this.unifyTypes(tuples.map((tuple) => tuple.fields[i]), generics, variants);
|
|
143
|
+
}
|
|
144
|
+
return unifiedTupleType;
|
|
145
|
+
}
|
|
146
|
+
case 'struct': {
|
|
147
|
+
const structs = types;
|
|
148
|
+
const ids = new Set(structs.map((struct) => Demonomorphizer.buildIdForStruct(struct.structType)));
|
|
149
|
+
if (ids.size > 1) {
|
|
150
|
+
// If the types are different structs, we can only unify them by creating a new binding.
|
|
151
|
+
// For example, if we have a struct A { x: u32 } and a struct A { x: Field }, the only possible unification is A<T> { x: T }
|
|
152
|
+
return this.buildBindingAndPushToVariants(types, generics, variants);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// If the types are the same struct, we must unify the arguments to the struct.
|
|
156
|
+
// For example, if we have A<Field> and A<u32>, we need to unify to A<T> and push T to the generics of the struct type.
|
|
157
|
+
const unifiedStruct = structs[0];
|
|
158
|
+
if (!structs.every((struct) => struct.args.length === unifiedStruct.args.length)) {
|
|
159
|
+
throw new Error('Same struct with different number of args encountered');
|
|
160
|
+
}
|
|
161
|
+
for (let i = 0; i < unifiedStruct.args.length; i++) {
|
|
162
|
+
const argTypes = structs.map((struct) => struct.args[i]);
|
|
163
|
+
unifiedStruct.args[i] = this.unifyTypes(argTypes, generics, variants);
|
|
164
|
+
}
|
|
165
|
+
return unifiedStruct;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
case 'constant': {
|
|
169
|
+
const constants = types;
|
|
170
|
+
if (constants.every((constant) => constant.value === constants[0].value)) {
|
|
171
|
+
return constants[0];
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
return this.buildBindingAndPushToVariants(types, generics, variants, true);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
default: {
|
|
178
|
+
const exhaustiveCheck = types[0];
|
|
179
|
+
throw new Error(`Unhandled abi type: ${exhaustiveCheck}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* We consider a struct to be the same if it has the same name and field names.
|
|
185
|
+
* Structs with the same id will be unified into a single type by the demonomorphizer.
|
|
186
|
+
*/
|
|
187
|
+
static buildIdForStruct(struct) {
|
|
188
|
+
const name = struct.path.split('::').pop();
|
|
189
|
+
const fields = struct.fields.map((field) => field.name).join(',');
|
|
190
|
+
return `${name}(${fields})`;
|
|
191
|
+
}
|
|
192
|
+
buildBindingAndPushToVariants(concreteTypes, generics, variants, isNumeric = false) {
|
|
193
|
+
const bindingId = new BindingId(this.lastBindingId++, isNumeric);
|
|
194
|
+
for (let i = 0; i < variants.length; i++) {
|
|
195
|
+
const variant = variants[i];
|
|
196
|
+
const concreteType = concreteTypes[i];
|
|
197
|
+
variant.args.push(concreteType);
|
|
198
|
+
}
|
|
199
|
+
generics.push(bindingId);
|
|
200
|
+
return { kind: 'binding', id: bindingId };
|
|
201
|
+
}
|
|
202
|
+
buildNumericBindingAndPushToVariants(concreteNumbers, generics, variants) {
|
|
203
|
+
const bindingId = new BindingId(this.lastBindingId++, true);
|
|
204
|
+
for (let i = 0; i < variants.length; i++) {
|
|
205
|
+
const variant = variants[i];
|
|
206
|
+
variant.args.push({ kind: 'constant', value: concreteNumbers[i] });
|
|
207
|
+
}
|
|
208
|
+
generics.push(bindingId);
|
|
209
|
+
return bindingId;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function allDeepEqual(arr) {
|
|
213
|
+
if (arr.length === 0) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
const first = JSON.stringify(arr[0]);
|
|
217
|
+
for (let i = 0; i < arr.length; i++) {
|
|
218
|
+
if (JSON.stringify(arr[i]) !== first) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function glob(cwd: string, patternsOrFiles: string[]): string[];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { CompiledCircuit } from '@noir-lang/types';
|
|
2
|
+
import { Abi } from '@noir-lang/noirc_abi';
|
|
3
|
+
export declare class TypingsGenerator {
|
|
4
|
+
private useFixedLengthArrays;
|
|
5
|
+
/** All the types in the ABIs */
|
|
6
|
+
private allTypes;
|
|
7
|
+
/** The demonomorphized ABIs of the circuits */
|
|
8
|
+
private demonomorphizedAbis;
|
|
9
|
+
/** Maps struct id to name for structs with the same name and different field sets */
|
|
10
|
+
private structIdToTsName;
|
|
11
|
+
/** Collect all the primitives used in the types to add them to the codegen */
|
|
12
|
+
private primitiveTypesUsed;
|
|
13
|
+
constructor(circuits: {
|
|
14
|
+
abi: Abi;
|
|
15
|
+
circuitName: string;
|
|
16
|
+
artifact?: CompiledCircuit;
|
|
17
|
+
}[], useFixedLengthArrays: boolean);
|
|
18
|
+
codegen(): string;
|
|
19
|
+
private codegenAllStructs;
|
|
20
|
+
private getStructName;
|
|
21
|
+
private codegenStructType;
|
|
22
|
+
private codegenType;
|
|
23
|
+
/**
|
|
24
|
+
* Typescript does not allow us to check for equality of non-primitive types
|
|
25
|
+
* easily, so we create a addIfUnique function that will only add an item
|
|
26
|
+
* to the map if it is not already there by using JSON.stringify.
|
|
27
|
+
* @param item - The item to add to the map.
|
|
28
|
+
*/
|
|
29
|
+
private addIfUnique;
|
|
30
|
+
/**
|
|
31
|
+
* Codegen all the interfaces for the circuits.
|
|
32
|
+
* For a circuit named Foo, we'll codegen FooInputType and FooReturnType.
|
|
33
|
+
*/
|
|
34
|
+
private codegenAllInterfaces;
|
|
35
|
+
private codegenAllPrimitives;
|
|
36
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { findAllStructsInType, mapAbiTypeToAbiTypeWithGenerics, } from './abi_type_with_generics.js';
|
|
2
|
+
import { Demonomorphizer } from './demonomorphizer.js';
|
|
3
|
+
const codegenPrelude = `/* Autogenerated file, do not edit! */
|
|
4
|
+
|
|
5
|
+
/* eslint-disable */
|
|
6
|
+
|
|
7
|
+
import { Noir, InputMap, CompiledCircuit, ForeignCallHandler } from "@noir-lang/noir_js"
|
|
8
|
+
|
|
9
|
+
export { ForeignCallHandler } from "@noir-lang/noir_js"
|
|
10
|
+
`;
|
|
11
|
+
/**
|
|
12
|
+
* Returns the last component of a path, e.g. "foo::bar::baz" -\> "baz"
|
|
13
|
+
* Note: that if we have a path such as "Baz", we will return "Baz".
|
|
14
|
+
*
|
|
15
|
+
* Since these paths corresponds to structs, we can assume that we
|
|
16
|
+
* cannot have "foo::bar::".
|
|
17
|
+
*
|
|
18
|
+
* We also make the assumption that since these paths are coming from
|
|
19
|
+
* Noir, then we will not have two paths that look like this:
|
|
20
|
+
* - foo::bar::Baz
|
|
21
|
+
* - cat::dog::Baz
|
|
22
|
+
* ie the last component of the path (struct name) is enough to uniquely identify
|
|
23
|
+
* the whole path.
|
|
24
|
+
*
|
|
25
|
+
* TODO: We should double check this assumption when we use type aliases,
|
|
26
|
+
* I expect that `foo::bar::Baz as Dog` would effectively give `foo::bar::Dog`
|
|
27
|
+
* @param str - The path to get the last component of.
|
|
28
|
+
* @returns The last component of the path.
|
|
29
|
+
*/
|
|
30
|
+
function getLastComponentOfPath(str) {
|
|
31
|
+
const parts = str.split('::');
|
|
32
|
+
const lastPart = parts[parts.length - 1];
|
|
33
|
+
return lastPart;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Replaces a numeric binding with the corresponding generics name or the actual value.
|
|
37
|
+
*/
|
|
38
|
+
function replaceNumericBinding(id, genericsNameMap) {
|
|
39
|
+
if (typeof id === 'number') {
|
|
40
|
+
return id.toString();
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return genericsNameMap.get(id.id) ?? 'unknown';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export class TypingsGenerator {
|
|
47
|
+
useFixedLengthArrays;
|
|
48
|
+
/** All the types in the ABIs */
|
|
49
|
+
allTypes = [];
|
|
50
|
+
/** The demonomorphized ABIs of the circuits */
|
|
51
|
+
demonomorphizedAbis = [];
|
|
52
|
+
/** Maps struct id to name for structs with the same name and different field sets */
|
|
53
|
+
structIdToTsName = new Map();
|
|
54
|
+
/** Collect all the primitives used in the types to add them to the codegen */
|
|
55
|
+
primitiveTypesUsed = new Map();
|
|
56
|
+
constructor(circuits, useFixedLengthArrays) {
|
|
57
|
+
this.useFixedLengthArrays = useFixedLengthArrays;
|
|
58
|
+
// Map all the types used in the ABIs to the demonomorphized types
|
|
59
|
+
for (const { abi, circuitName, artifact } of circuits) {
|
|
60
|
+
const params = abi.parameters.map((param) => {
|
|
61
|
+
const type = mapAbiTypeToAbiTypeWithGenerics(param.type);
|
|
62
|
+
this.allTypes.push(type);
|
|
63
|
+
return { name: param.name, type };
|
|
64
|
+
});
|
|
65
|
+
if (abi.return_type) {
|
|
66
|
+
const returnType = mapAbiTypeToAbiTypeWithGenerics(abi.return_type.abi_type);
|
|
67
|
+
this.allTypes.push(returnType);
|
|
68
|
+
this.demonomorphizedAbis.push({ circuitName, params, returnType, artifact });
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this.demonomorphizedAbis.push({ circuitName, params, artifact });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Demonomorphize the types
|
|
75
|
+
Demonomorphizer.demonomorphize(this.allTypes, {
|
|
76
|
+
leaveArrayLengthsUnbounded: !useFixedLengthArrays,
|
|
77
|
+
leaveStringLengthsUnbounded: true,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
codegen() {
|
|
81
|
+
this.primitiveTypesUsed = new Map();
|
|
82
|
+
const structsCode = this.codegenAllStructs();
|
|
83
|
+
const interfacesCode = this.codegenAllInterfaces();
|
|
84
|
+
const primitivesCode = this.codegenAllPrimitives();
|
|
85
|
+
return `
|
|
86
|
+
${codegenPrelude}
|
|
87
|
+
${primitivesCode}
|
|
88
|
+
${structsCode}
|
|
89
|
+
${interfacesCode}`;
|
|
90
|
+
}
|
|
91
|
+
codegenAllStructs() {
|
|
92
|
+
const allStructs = this.allTypes.flatMap(findAllStructsInType);
|
|
93
|
+
// First, deduplicate the structs used
|
|
94
|
+
const structTypesToExport = new Map();
|
|
95
|
+
for (const struct of allStructs) {
|
|
96
|
+
const id = Demonomorphizer.buildIdForStruct(struct.structType);
|
|
97
|
+
if (structTypesToExport.has(id)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
structTypesToExport.set(id, struct.structType);
|
|
101
|
+
}
|
|
102
|
+
// Then, we have to consider the case where we have struct with the same name but different fields.
|
|
103
|
+
// For those, we'll naively append a number to the name.
|
|
104
|
+
const idsPerName = new Map();
|
|
105
|
+
for (const [id, structType] of structTypesToExport.entries()) {
|
|
106
|
+
const name = getLastComponentOfPath(structType.path);
|
|
107
|
+
const ids = idsPerName.get(name) ?? [];
|
|
108
|
+
ids.push(id);
|
|
109
|
+
idsPerName.set(name, ids);
|
|
110
|
+
}
|
|
111
|
+
this.structIdToTsName = new Map();
|
|
112
|
+
for (const [name, ids] of idsPerName.entries()) {
|
|
113
|
+
if (ids.length !== 1) {
|
|
114
|
+
ids.forEach((id, index) => {
|
|
115
|
+
this.structIdToTsName.set(id, `${name}${index + 1}`);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Now we can just generate the code for the structs
|
|
120
|
+
let resultCode = '';
|
|
121
|
+
for (const structType of structTypesToExport.values()) {
|
|
122
|
+
resultCode += this.codegenStructType(structType);
|
|
123
|
+
}
|
|
124
|
+
return resultCode;
|
|
125
|
+
}
|
|
126
|
+
getStructName(structType) {
|
|
127
|
+
return (this.structIdToTsName.get(Demonomorphizer.buildIdForStruct(structType)) || getLastComponentOfPath(structType.path));
|
|
128
|
+
}
|
|
129
|
+
codegenStructType(structType) {
|
|
130
|
+
// Generate names for the generic bindings.
|
|
131
|
+
const genericsNameMap = new Map();
|
|
132
|
+
structType.generics.forEach((generic, index) => {
|
|
133
|
+
genericsNameMap.set(generic.id, String.fromCharCode('A'.charCodeAt(0) + index));
|
|
134
|
+
});
|
|
135
|
+
const name = this.getStructName(structType);
|
|
136
|
+
const generics = structType.generics.length
|
|
137
|
+
? `<${structType.generics
|
|
138
|
+
.map((generic) => `${genericsNameMap.get(generic.id)}${generic.isNumeric ? ' extends number' : ''}`)
|
|
139
|
+
.join(', ')}>`
|
|
140
|
+
: '';
|
|
141
|
+
let resultCode = `export type ${name}${generics} = {\n`;
|
|
142
|
+
for (const field of structType.fields) {
|
|
143
|
+
resultCode += ` ${field.name}: ${this.codegenType(field.type, genericsNameMap)};\n`;
|
|
144
|
+
}
|
|
145
|
+
resultCode += '}\n\n';
|
|
146
|
+
return resultCode;
|
|
147
|
+
}
|
|
148
|
+
codegenType(type, genericsNameMap) {
|
|
149
|
+
switch (type.kind) {
|
|
150
|
+
case 'field':
|
|
151
|
+
this.addIfUnique({ aliasName: 'Field', tsType: 'string' });
|
|
152
|
+
return 'Field';
|
|
153
|
+
case 'boolean':
|
|
154
|
+
return 'boolean';
|
|
155
|
+
case 'integer': {
|
|
156
|
+
const typeName = type.sign === 'signed' ? `i${type.width}` : `u${type.width}`;
|
|
157
|
+
// Even though noir accepts numbers or strings for integers, it always returns strings
|
|
158
|
+
// So we must use string as the type here.
|
|
159
|
+
this.addIfUnique({ aliasName: typeName, tsType: 'string' });
|
|
160
|
+
return typeName;
|
|
161
|
+
}
|
|
162
|
+
case 'binding':
|
|
163
|
+
return genericsNameMap.get(type.id.id) ?? 'unknown';
|
|
164
|
+
case 'constant':
|
|
165
|
+
return type.value.toString();
|
|
166
|
+
case 'string':
|
|
167
|
+
return `string`;
|
|
168
|
+
case 'array':
|
|
169
|
+
if (this.useFixedLengthArrays) {
|
|
170
|
+
if (type.length === null) {
|
|
171
|
+
throw new Error('Got unbounded array with fixed length arrays enabled');
|
|
172
|
+
}
|
|
173
|
+
return `FixedLengthArray<${this.codegenType(type.type, genericsNameMap)}, ${replaceNumericBinding(type.length, genericsNameMap)}>`;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
return `${this.codegenType(type.type, genericsNameMap)}[]`;
|
|
177
|
+
}
|
|
178
|
+
case 'tuple': {
|
|
179
|
+
const fieldTypes = type.fields.map((field) => this.codegenType(field, genericsNameMap));
|
|
180
|
+
return `[${fieldTypes.join(', ')}]`;
|
|
181
|
+
}
|
|
182
|
+
case 'struct': {
|
|
183
|
+
const name = this.getStructName(type.structType);
|
|
184
|
+
if (type.args.length) {
|
|
185
|
+
const args = type.args.map((arg) => this.codegenType(arg, genericsNameMap)).join(', ');
|
|
186
|
+
return `${name}<${args}>`;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
return name;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Typescript does not allow us to check for equality of non-primitive types
|
|
196
|
+
* easily, so we create a addIfUnique function that will only add an item
|
|
197
|
+
* to the map if it is not already there by using JSON.stringify.
|
|
198
|
+
* @param item - The item to add to the map.
|
|
199
|
+
*/
|
|
200
|
+
addIfUnique(item) {
|
|
201
|
+
const key = JSON.stringify(item);
|
|
202
|
+
if (!this.primitiveTypesUsed.has(key)) {
|
|
203
|
+
this.primitiveTypesUsed.set(key, item);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Codegen all the interfaces for the circuits.
|
|
208
|
+
* For a circuit named Foo, we'll codegen FooInputType and FooReturnType.
|
|
209
|
+
*/
|
|
210
|
+
codegenAllInterfaces() {
|
|
211
|
+
let resultCode = '';
|
|
212
|
+
for (const { circuitName, params, returnType, artifact } of this.demonomorphizedAbis) {
|
|
213
|
+
const functionSignature = {
|
|
214
|
+
inputs: params.map((param) => [param.name, this.codegenType(param.type, new Map())]),
|
|
215
|
+
returnValue: returnType ? this.codegenType(returnType, new Map()) : null,
|
|
216
|
+
};
|
|
217
|
+
resultCode += this.codegenStructType({
|
|
218
|
+
path: `${circuitName}InputType`,
|
|
219
|
+
fields: params,
|
|
220
|
+
generics: [],
|
|
221
|
+
});
|
|
222
|
+
if (returnType) {
|
|
223
|
+
resultCode += `export type ${circuitName}ReturnType = ${this.codegenType(returnType, new Map())};\n`;
|
|
224
|
+
}
|
|
225
|
+
resultCode += codegenFunction(circuitName, functionSignature, artifact);
|
|
226
|
+
}
|
|
227
|
+
return resultCode;
|
|
228
|
+
}
|
|
229
|
+
codegenAllPrimitives() {
|
|
230
|
+
let primitiveTypeAliases = this.useFixedLengthArrays
|
|
231
|
+
? 'export type FixedLengthArray<T, L extends number> = L extends 0 ? never[]: T[] & { length: L }\n'
|
|
232
|
+
: '';
|
|
233
|
+
for (const [, value] of this.primitiveTypesUsed) {
|
|
234
|
+
primitiveTypeAliases += `export type ${value.aliasName} = ${value.tsType};\n`;
|
|
235
|
+
}
|
|
236
|
+
return primitiveTypeAliases;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const codegenFunction = (name, function_signature, compiled_program) => {
|
|
240
|
+
const args = function_signature.inputs.map(([name]) => `${name}`).join(', ');
|
|
241
|
+
const args_with_types = function_signature.inputs.map(([name, type]) => `${name}: ${type}`).join(', ');
|
|
242
|
+
const artifact = compiled_program
|
|
243
|
+
? `export const ${name}_circuit: CompiledCircuit = ${JSON.stringify(compiled_program)};`
|
|
244
|
+
: '';
|
|
245
|
+
return `${artifact}
|
|
246
|
+
|
|
247
|
+
export async function ${name}(${args_with_types}${compiled_program ? '' : `, ${name}_circuit: CompiledCircuit`}, foreignCallHandler?: ForeignCallHandler): Promise<${function_signature.returnValue}> {
|
|
248
|
+
const program = new Noir(${name}_circuit);
|
|
249
|
+
const args: InputMap = { ${args} };
|
|
250
|
+
const { returnValue } = await program.execute(args, foreignCallHandler);
|
|
251
|
+
return returnValue as ${function_signature.returnValue};
|
|
252
|
+
}
|
|
253
|
+
`;
|
|
254
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aztec/noir-noir_codegen",
|
|
3
|
+
"contributors": [
|
|
4
|
+
"The Noir Team <team@noir-lang.org>"
|
|
5
|
+
],
|
|
6
|
+
"version": "0.75.0-commit.8a71f57856e217a77b6e50cbc8833c1cd5395b96",
|
|
7
|
+
"packageManager": "yarn@3.5.1",
|
|
8
|
+
"license": "(MIT OR Apache-2.0)",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"homepage": "https://noir-lang.org/",
|
|
11
|
+
"repository": {
|
|
12
|
+
"url": "https://github.com/noir-lang/noir.git",
|
|
13
|
+
"directory": "compiler/wasm",
|
|
14
|
+
"type": "git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/noir-lang/noir/issues"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"glob": "^10.3.10",
|
|
21
|
+
"ts-command-line-args": "^2.5.1",
|
|
22
|
+
"@aztec/noir-types": "0.75.0-commit.8a71f57856e217a77b6e50cbc8833c1cd5395b96"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"lib",
|
|
26
|
+
"package.json"
|
|
27
|
+
],
|
|
28
|
+
"source": "src/index.ts",
|
|
29
|
+
"main": "lib/index.js",
|
|
30
|
+
"types": "lib/index.d.ts",
|
|
31
|
+
"bin": {
|
|
32
|
+
"noir-codegen": "lib/main.js"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"dev": "tsc-multi --watch",
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"test": "yarn test:codegen && yarn test:node && yarn test:clean",
|
|
38
|
+
"test:codegen": "${NARGO:-nargo} export --program-dir=./test/test_lib && tsx src/main.ts ./test/test_lib/export/** --out-dir ./test/codegen",
|
|
39
|
+
"test:node": "mocha --timeout 25000 --exit --config ./.mocharc.json",
|
|
40
|
+
"test:clean": "rm -rf ./test/codegen ./test/test_lib/export",
|
|
41
|
+
"prettier": "prettier 'src/**/*.ts'",
|
|
42
|
+
"prettier:fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'",
|
|
43
|
+
"lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0",
|
|
44
|
+
"nightly:version": "jq --arg new_version \"-$(git rev-parse --short HEAD)$1\" '.version = .version + $new_version' package.json > package-tmp.json && mv package-tmp.json package.json",
|
|
45
|
+
"publish": "echo 📡 publishing `$npm_package_name` && yarn npm publish",
|
|
46
|
+
"clean": "rm -rf ./lib"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@noir-lang/noir_js": "1.0.0-beta.1",
|
|
50
|
+
"@types/chai": "^4",
|
|
51
|
+
"@types/mocha": "^10.0.1",
|
|
52
|
+
"@types/node": "^20.6.2",
|
|
53
|
+
"@types/prettier": "^3",
|
|
54
|
+
"chai": "^4.4.1",
|
|
55
|
+
"eslint": "^8.57.0",
|
|
56
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
57
|
+
"mocha": "^10.2.0",
|
|
58
|
+
"prettier": "3.2.5",
|
|
59
|
+
"ts-node": "^10.9.1",
|
|
60
|
+
"tsx": "^4.6.2",
|
|
61
|
+
"typescript": "^5.4.2"
|
|
62
|
+
}
|
|
63
|
+
}
|