@graphprotocol/graph-cli 0.49.0-alpha-20230504181204-a99dddd → 0.49.0-alpha-20230505103249-d3fbf0b

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @graphprotocol/graph-cli
2
2
 
3
- ## 0.49.0-alpha-20230504181204-a99dddd
3
+ ## 0.49.0-alpha-20230505103249-d3fbf0b
4
4
 
5
5
  ### Minor Changes
6
6
 
@@ -9,49 +9,11 @@
9
9
  Thanks [@saihaj](https://github.com/saihaj)! - do not generate types, instead show a message to
10
10
  user to use substreams CLI for codegen.
11
11
 
12
- - [#1335](https://github.com/graphprotocol/graph-tooling/pull/1335)
13
- [`6bd042b`](https://github.com/graphprotocol/graph-tooling/commit/6bd042bb5dd8facff02450c874e8811aebc83fcc)
14
- Thanks [@saihaj](https://github.com/saihaj)! - For substreams generate the directory tree as
15
- follows
16
-
17
- ```
18
- .
19
- ├── package.json
20
- ├── schema.graphql
21
- └── subgraph.yaml
22
- ```
23
-
24
- In the `package.json` we only depend on the CLI since that is what developer will use to deploy
25
- the subgraph. The `schema.graphql` is the schema of the subgraph and the `subgraph.yaml` is the
26
- manifest file for the subgraph.
27
-
28
- - [#1335](https://github.com/graphprotocol/graph-tooling/pull/1335)
29
- [`f90dd75`](https://github.com/graphprotocol/graph-tooling/commit/f90dd757a558f4e66cc46780f13612309f45646a)
30
- Thanks [@saihaj](https://github.com/saihaj)! - remove index events as entities for substreams
31
-
32
12
  - [#1306](https://github.com/graphprotocol/graph-tooling/pull/1306)
33
13
  [`f5e4b58`](https://github.com/graphprotocol/graph-tooling/commit/f5e4b58989edc5f3bb8211f1b912449e77832de8)
34
14
  Thanks [@saihaj](https://github.com/saihaj)! - Add loadInBlock function for entities as part of
35
15
  codegen
36
16
 
37
- - [#1335](https://github.com/graphprotocol/graph-tooling/pull/1335)
38
- [`f90dd75`](https://github.com/graphprotocol/graph-tooling/commit/f90dd757a558f4e66cc46780f13612309f45646a)
39
- Thanks [@saihaj](https://github.com/saihaj)! - remove start block selection for substreams
40
-
41
- ### Patch Changes
42
-
43
- - [#1335](https://github.com/graphprotocol/graph-tooling/pull/1335)
44
- [`aac26c9`](https://github.com/graphprotocol/graph-tooling/commit/aac26c97b5427156f8d61311924bac215155b4cb)
45
- Thanks [@saihaj](https://github.com/saihaj)! - dependencies updates:
46
-
47
- - Updated dependency [`gluegun@5.1.2` ↗︎](https://www.npmjs.com/package/gluegun/v/5.1.2) (from
48
- `https://github.com/edgeandnode/gluegun#v4.3.1-pin-colors-dep`, in `dependencies`)
49
-
50
- - [#1331](https://github.com/graphprotocol/graph-tooling/pull/1331)
51
- [`b2c8dec`](https://github.com/graphprotocol/graph-tooling/commit/b2c8decb7806d25df6ca4e07f6dfdf7941516435)
52
- Thanks [@saihaj](https://github.com/saihaj)! - only allow mainnet for substreams in network
53
- selection for init
54
-
55
17
  ## 0.48.0
56
18
 
57
19
  ### Minor Changes
@@ -21,13 +21,20 @@ export default class SchemaCodeGenerator {
21
21
  constructor(schema: Schema);
22
22
  generateModuleImports(): tsCodegen.ModuleImports[];
23
23
  generateTypes(): Array<tsCodegen.Class>;
24
+ generateDerivedLoaders(): any[];
24
25
  _isEntityTypeDefinition(def: DefinitionNode): def is ObjectTypeDefinitionNode;
26
+ _isDerivedField(field: any): boolean;
25
27
  _isInterfaceDefinition(def: DefinitionNode): def is InterfaceTypeDefinitionNode;
26
28
  _generateEntityType(def: ObjectTypeDefinitionNode): tsCodegen.Class;
29
+ _generateDerivedLoader(typeName: string): any;
30
+ _getTypeNameForField(gqlType: TypeNode): string;
27
31
  _generateConstructor(_entityName: string, fields: readonly FieldDefinitionNode[] | undefined): tsCodegen.Method;
28
32
  _generateStoreMethods(entityName: string, idField: IdField): Array<tsCodegen.Method | tsCodegen.StaticMethod>;
29
33
  _generateEntityFieldMethods(entityDef: ObjectTypeDefinitionNode, fieldDef: FieldDefinitionNode): Array<tsCodegen.Method>;
30
34
  _generateEntityFieldGetter(_entityDef: ObjectTypeDefinitionNode, fieldDef: FieldDefinitionNode): tsCodegen.Method;
35
+ _generateDerivedFieldGetter(entityDef: any, fieldDef: FieldDefinitionNode): tsCodegen.Method;
36
+ _returnTypeForDervied(gqlType: any): any;
37
+ _generatedEntityDerivedFieldGetter(_entityDef: any, fieldDef: FieldDefinitionNode): tsCodegen.Method;
31
38
  _generateEntityFieldSetter(_entityDef: ObjectTypeDefinitionNode, fieldDef: FieldDefinitionNode): tsCodegen.Method | null;
32
39
  _resolveFieldType(gqlType: NamedTypeNode): string;
33
40
  /** Return the type that values for this field must have. For scalar
@@ -22,7 +22,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
+ /* eslint-disable unicorn/no-array-for-each */
30
+ const debug_1 = __importDefault(require("debug"));
26
31
  const typesCodegen = __importStar(require("./types"));
27
32
  const tsCodegen = __importStar(require("./typescript"));
28
33
  class IdField {
@@ -98,10 +103,24 @@ class SchemaCodeGenerator {
98
103
  })
99
104
  .filter(Boolean);
100
105
  }
106
+ generateDerivedLoaders() {
107
+ const fields = this.schema.ast.definitions
108
+ .filter(def => this._isEntityTypeDefinition(def))
109
+ .flatMap((def) => def.fields)
110
+ .filter((def) => this._isDerivedField(def))
111
+ .map((def) => this._getTypeNameForField(def.type));
112
+ return [...new Set(fields)].map((typeName) => {
113
+ return this._generateDerivedLoader(typeName);
114
+ });
115
+ }
101
116
  _isEntityTypeDefinition(def) {
102
117
  return (def.kind === 'ObjectTypeDefinition' &&
103
118
  def.directives?.find(directive => directive.name.value === 'entity') !== undefined);
104
119
  }
120
+ _isDerivedField(field) {
121
+ return (field.directives?.find((directive) => directive.name.value === 'derivedFrom') !==
122
+ undefined);
123
+ }
105
124
  _isInterfaceDefinition(def) {
106
125
  return def.kind === 'InterfaceTypeDefinition';
107
126
  }
@@ -120,6 +139,42 @@ class SchemaCodeGenerator {
120
139
  .forEach((method) => klass.addMethod(method));
121
140
  return klass;
122
141
  }
142
+ _generateDerivedLoader(typeName) {
143
+ // <field>Loader
144
+ const klass = tsCodegen.klass(`${typeName}Loader`, { export: true, extends: 'Entity' });
145
+ klass.addMember(tsCodegen.klassMember('_entity', 'string'));
146
+ klass.addMember(tsCodegen.klassMember('_field', 'string'));
147
+ klass.addMember(tsCodegen.klassMember('_id', 'string'));
148
+ // Generate and add a constructor
149
+ klass.addMethod(tsCodegen.method('constructor', [
150
+ tsCodegen.param('entity', 'string'),
151
+ tsCodegen.param('id', 'string'),
152
+ tsCodegen.param('field', 'string'),
153
+ ], undefined, `
154
+ super();
155
+ this._entity = entity;
156
+ this._id = id;
157
+ this._field = field;
158
+ `));
159
+ // Generate load() method for the Loader
160
+ klass.addMethod(tsCodegen.method('load', [], `${typeName}[]`, `
161
+ let value = store.loadRelated(this._entity, this._id, this._field);
162
+ return changetype<${typeName}[]>(value);
163
+ `));
164
+ return klass;
165
+ }
166
+ _getTypeNameForField(gqlType) {
167
+ if (gqlType.kind === 'NonNullType') {
168
+ return this._getTypeNameForField(gqlType.type);
169
+ }
170
+ if (gqlType.kind === 'ListType') {
171
+ return this._getTypeNameForField(gqlType.type);
172
+ }
173
+ if (gqlType.kind === 'NamedType') {
174
+ return gqlType.name.value;
175
+ }
176
+ throw new Error(`Unknown type kind: ${gqlType}`);
177
+ }
123
178
  _generateConstructor(_entityName, fields) {
124
179
  const idField = IdField.fromFields(fields);
125
180
  return tsCodegen.method('constructor', [tsCodegen.param('id', idField.tsNamedType())], undefined, `
@@ -156,6 +211,12 @@ class SchemaCodeGenerator {
156
211
  .filter(Boolean);
157
212
  }
158
213
  _generateEntityFieldGetter(_entityDef, fieldDef) {
214
+ const isDerivedField = this._isDerivedField(fieldDef);
215
+ const codegenDebug = (0, debug_1.default)('codegen');
216
+ if (isDerivedField) {
217
+ codegenDebug(`Generating derived field getter for ${fieldDef.name.value}`);
218
+ return this._generateDerivedFieldGetter(_entityDef, fieldDef);
219
+ }
159
220
  const name = fieldDef.name.value;
160
221
  const gqlType = fieldDef.type;
161
222
  const fieldValueType = this._valueTypeFromGraphQl(gqlType);
@@ -179,6 +240,42 @@ class SchemaCodeGenerator {
179
240
  ${isNullable ? getNullable : getNonNullable}
180
241
  `);
181
242
  }
243
+ _generateDerivedFieldGetter(entityDef, fieldDef) {
244
+ const entityName = entityDef.name.value;
245
+ const name = fieldDef.name.value;
246
+ const gqlType = fieldDef.type;
247
+ const returnType = this._returnTypeForDervied(gqlType);
248
+ return tsCodegen.method(`get ${name}`, [], returnType, `
249
+ return new ${returnType}('${entityName}', this.get('id')!.toString(), '${name}')
250
+ `);
251
+ }
252
+ _returnTypeForDervied(gqlType) {
253
+ if (gqlType.kind === 'NonNullType') {
254
+ return this._returnTypeForDervied(gqlType.type);
255
+ }
256
+ if (gqlType.kind === 'ListType') {
257
+ return this._returnTypeForDervied(gqlType.type);
258
+ }
259
+ const type = tsCodegen.namedType(gqlType.name.value + 'Loader');
260
+ return type;
261
+ }
262
+ _generatedEntityDerivedFieldGetter(_entityDef, fieldDef) {
263
+ const name = fieldDef.name.value;
264
+ const gqlType = fieldDef.type;
265
+ const fieldValueType = this._valueTypeFromGraphQl(gqlType);
266
+ const returnType = this._typeFromGraphQl(gqlType);
267
+ const isNullable = returnType instanceof tsCodegen.NullableType;
268
+ const getNonNullable = `return ${typesCodegen.valueToAsc('value!', fieldValueType)}`;
269
+ const getNullable = `if (!value || value.kind == ValueKind.NULL) {
270
+ return null
271
+ } else {
272
+ return ${typesCodegen.valueToAsc('value', fieldValueType)}
273
+ }`;
274
+ return tsCodegen.method(`get ${name}`, [], returnType, `
275
+ let value = this.get('${name}')
276
+ ${isNullable ? getNullable : getNonNullable}
277
+ `);
278
+ }
182
279
  _generateEntityFieldSetter(_entityDef, fieldDef) {
183
280
  const name = fieldDef.name.value;
184
281
  const isDerivedField = !!fieldDef.directives?.find(directive => directive.name.value === 'derivedFrom');
@@ -276,14 +276,9 @@ describe('Schema code generator', () => {
276
276
  {
277
277
  name: 'get wallets',
278
278
  params: [],
279
- returnType: new typescript_1.NullableType(new typescript_1.ArrayType(new typescript_1.NamedType('string'))),
279
+ returnType: new typescript_1.NamedType('WalletLoader'),
280
280
  body: `
281
- let value = this.get('wallets')
282
- if (!value || value.kind == ValueKind.NULL) {
283
- return null
284
- } else {
285
- return value.toStringArray()
286
- }
281
+ return new WalletLoader("Account", this.get('id')!.toString(), "wallets")
287
282
  `,
288
283
  },
289
284
  ],
@@ -14,14 +14,6 @@ export declare const generateScaffold: ({ protocolInstance, abi, contract, netwo
14
14
  startBlock?: string | undefined;
15
15
  node?: string | undefined;
16
16
  }, spinner: Spinner) => Promise<{
17
- 'subgraph.yaml': string;
18
- 'schema.graphql': string;
19
- 'package.json': string;
20
- 'tsconfig.json'?: undefined;
21
- src?: undefined;
22
- abis?: undefined;
23
- tests?: undefined;
24
- } | {
25
17
  'package.json': string;
26
18
  'subgraph.yaml': string;
27
19
  'schema.graphql': string;
@@ -39,10 +39,12 @@ const scaffold_1 = require("../command-helpers/scaffold");
39
39
  const spinner_1 = require("../command-helpers/spinner");
40
40
  const studio_1 = require("../command-helpers/studio");
41
41
  const subgraph_1 = require("../command-helpers/subgraph");
42
+ const debug_1 = __importDefault(require("../debug"));
42
43
  const protocols_1 = __importDefault(require("../protocols"));
43
44
  const schema_1 = require("../scaffold/schema");
44
45
  const validation_1 = require("../validation");
45
46
  const add_1 = __importDefault(require("./add"));
47
+ const initDebug = (0, debug_1.default)('graph-cli:init');
46
48
  const protocolChoices = Array.from(protocols_1.default.availableProtocols().keys());
47
49
  const availableNetworks = protocols_1.default.availableNetworks();
48
50
  const DEFAULT_EXAMPLE_SUBGRAPH = 'ethereum/gravatar';
@@ -83,18 +85,8 @@ class InitCommand extends core_1.Command {
83
85
  };
84
86
  // If all parameters are provided from the command-line,
85
87
  // go straight to creating the subgraph from the example
86
- if (fromExample && subgraphName && directory && protocol) {
87
- await initSubgraphFromExample.bind(this)({
88
- fromExample,
89
- allowSimpleName,
90
- directory,
91
- subgraphName,
92
- studio,
93
- product,
94
- protocolInstance: new protocols_1.default(protocol),
95
- }, { commands });
96
- // Exit with success
97
- return this.exit(0);
88
+ if (fromExample && subgraphName && directory) {
89
+ return await initSubgraphFromExample.bind(this)({ fromExample, allowSimpleName, directory, subgraphName, studio, product }, { commands });
98
90
  }
99
91
  // Will be assigned below if ethereum
100
92
  let abi;
@@ -130,7 +122,7 @@ class InitCommand extends core_1.Command {
130
122
  }
131
123
  }
132
124
  }
133
- await initSubgraphFromContract.bind(this)({
125
+ return await initSubgraphFromContract.bind(this)({
134
126
  protocolInstance,
135
127
  abi,
136
128
  allowSimpleName,
@@ -145,8 +137,6 @@ class InitCommand extends core_1.Command {
145
137
  product,
146
138
  startBlock,
147
139
  }, { commands, addContract: false });
148
- // Exit with success
149
- return this.exit(0);
150
140
  }
151
141
  // Otherwise, take the user through the interactive form
152
142
  const answers = await processInitForm.bind(this)({
@@ -176,7 +166,6 @@ class InitCommand extends core_1.Command {
176
166
  directory: answers.directory,
177
167
  studio: answers.studio,
178
168
  product: answers.product,
179
- protocolInstance: answers.protocolInstance,
180
169
  }, { commands });
181
170
  }
182
171
  else {
@@ -202,8 +191,6 @@ class InitCommand extends core_1.Command {
202
191
  startBlock: answers.startBlock,
203
192
  }, { commands, addContract: true });
204
193
  }
205
- // Exit with success
206
- this.exit(0);
207
194
  }
208
195
  }
209
196
  InitCommand.description = 'Creates a new subgraph with basic scaffolding.';
@@ -278,224 +265,227 @@ InitCommand.flags = {
278
265
  }),
279
266
  };
280
267
  exports.default = InitCommand;
281
- async function processInitForm({ protocol: initProtocol, product: initProduct, studio: initStudio, node: initNode, abi: initAbi, directory: initDirectory, contract: initContract, indexEvents: initIndexEvents, fromExample: initFromExample, network: initNetwork, subgraphName: initSubgraphName, contractName: initContractName, startBlock: initStartBlock, allowSimpleName: initAllowSimpleName, }) {
268
+ async function processInitForm({ protocol, product, studio, node, abi, allowSimpleName, directory, contract, indexEvents, fromExample, network, subgraphName, contractName, startBlock, }) {
282
269
  let abiFromEtherscan = undefined;
283
- try {
284
- const { protocol } = await gluegun_1.prompt.ask({
270
+ let abiFromFile = undefined;
271
+ let protocolInstance;
272
+ let ProtocolContract;
273
+ let ABI;
274
+ const questions = [
275
+ {
285
276
  type: 'select',
286
277
  name: 'protocol',
287
278
  message: 'Protocol',
288
279
  choices: protocolChoices,
289
- skip: protocolChoices.includes(String(initProtocol)),
290
- });
291
- const protocolInstance = new protocols_1.default(protocol);
292
- const isSubstreams = protocol === 'substreams';
293
- const { product } = await gluegun_1.prompt.ask([
294
- {
295
- type: 'select',
296
- name: 'product',
297
- message: 'Product for which to initialize',
298
- choices: ['subgraph-studio', 'hosted-service'],
299
- skip: protocol === 'arweave' ||
300
- protocol === 'cosmos' ||
301
- protocol === 'near' ||
302
- initProduct === 'subgraph-studio' ||
303
- initProduct === 'hosted-service' ||
304
- initStudio !== undefined ||
305
- initNode !== undefined,
306
- result: value => {
307
- if (initProduct)
308
- return initProduct;
309
- if (initStudio)
310
- return 'subgraph-studio';
311
- // For now we only support NEAR subgraphs in the Hosted Service
312
- if (protocol === 'near') {
313
- return 'hosted-service';
314
- }
315
- if (value == 'subgraph-studio') {
316
- initAllowSimpleName = true;
317
- }
318
- return value;
319
- },
280
+ skip: protocolChoices.includes(String(protocol)),
281
+ result: (value) => {
282
+ // eslint-disable-next-line -- prettier has problems with ||=
283
+ protocol = protocol || value;
284
+ protocolInstance = new protocols_1.default(protocol);
285
+ return protocol;
320
286
  },
321
- ]);
322
- const { subgraphName } = await gluegun_1.prompt.ask([
323
- {
324
- type: 'input',
325
- name: 'subgraphName',
326
- message: () => (product == 'subgraph-studio' ? 'Subgraph slug' : 'Subgraph name'),
327
- initial: initSubgraphName,
328
- validate: name => {
329
- try {
330
- (0, subgraph_1.validateSubgraphName)(name, { allowSimpleName: initAllowSimpleName });
331
- return true;
332
- }
333
- catch (e) {
334
- return `${e.message}
287
+ },
288
+ {
289
+ type: 'select',
290
+ name: 'product',
291
+ message: 'Product for which to initialize',
292
+ choices: ['subgraph-studio', 'hosted-service'],
293
+ skip: () => protocol === 'arweave' ||
294
+ protocol === 'cosmos' ||
295
+ protocol === 'near' ||
296
+ product === 'subgraph-studio' ||
297
+ product === 'hosted-service' ||
298
+ studio !== undefined ||
299
+ node !== undefined,
300
+ result: (value) => {
301
+ // For now we only support NEAR subgraphs in the Hosted Service
302
+ if (protocol === 'near') {
303
+ // Can be overwritten because the question will be skipped (product === undefined)
304
+ product = 'hosted-service';
305
+ return product;
306
+ }
307
+ if (value == 'subgraph-studio') {
308
+ allowSimpleName = true;
309
+ }
310
+ product = value;
311
+ return value;
312
+ },
313
+ },
314
+ {
315
+ type: 'input',
316
+ name: 'subgraphName',
317
+ message: () => (product == 'subgraph-studio' || studio ? 'Subgraph slug' : 'Subgraph name'),
318
+ initial: subgraphName,
319
+ validate: (name) => {
320
+ try {
321
+ (0, subgraph_1.validateSubgraphName)(name, { allowSimpleName });
322
+ return true;
323
+ }
324
+ catch (e) {
325
+ return `${e.message}
335
326
 
336
- Examples:
327
+ Examples:
337
328
 
338
- $ graph init ${os_1.default.userInfo().username}/${name}
339
- $ graph init ${name} --allow-simple-name`;
340
- }
341
- },
329
+ $ graph init ${os_1.default.userInfo().username}/${name}
330
+ $ graph init ${name} --allow-simple-name`;
331
+ }
342
332
  },
343
- ]);
344
- const { directory } = await gluegun_1.prompt.ask([
345
- {
346
- type: 'input',
347
- name: 'directory',
348
- message: 'Directory to create the subgraph in',
349
- initial: () => initDirectory || (0, subgraph_1.getSubgraphBasename)(subgraphName),
350
- validate: value => gluegun_1.filesystem.exists(value || initDirectory || (0, subgraph_1.getSubgraphBasename)(subgraphName))
351
- ? 'Directory already exists'
352
- : true,
333
+ result: (value) => {
334
+ subgraphName = value;
335
+ return value;
353
336
  },
354
- ]);
355
- const { network } = await gluegun_1.prompt.ask([
356
- {
357
- type: 'select',
358
- name: 'network',
359
- message: () => `${protocolInstance.displayName()} network`,
360
- choices: availableNetworks
337
+ },
338
+ {
339
+ type: 'input',
340
+ name: 'directory',
341
+ message: 'Directory to create the subgraph in',
342
+ initial: () => directory ||
343
+ (0, subgraph_1.getSubgraphBasename)(
344
+ // @ts-expect-error will be set by previous question
345
+ subgraphName),
346
+ validate: (value) => gluegun_1.filesystem.exists(value ||
347
+ directory ||
348
+ (0, subgraph_1.getSubgraphBasename)(
349
+ // @ts-expect-error will be set by previous question
350
+ subgraphName))
351
+ ? 'Directory already exists'
352
+ : true,
353
+ },
354
+ {
355
+ type: 'select',
356
+ name: 'network',
357
+ message: () => `${protocolInstance.displayName()} network`,
358
+ choices: () => {
359
+ initDebug('Generating list of available networks for protocol "%s" (%M)', protocol, availableNetworks.get(protocol));
360
+ return (
361
+ // @ts-expect-error TODO: wait what?
362
+ availableNetworks
361
363
  .get(protocol) // Get networks related to the chosen protocol.
362
- ?.toArray() || ['mainnet'],
363
- skip: initFromExample !== undefined,
364
- result: value => {
365
- if (initNetwork)
366
- return initNetwork;
367
- return value;
368
- },
364
+ .toArray()); // Needed because of gluegun. It can't even receive a JS iterable.
369
365
  },
370
- ]);
371
- const { contract } = await gluegun_1.prompt.ask([
372
- // TODO:
373
- // protocols that don't support contract
374
- // - arweave
375
- // - cosmos
376
- {
377
- type: 'input',
378
- name: 'contract',
379
- message: `Contract ${protocolInstance.getContract()?.identifierName()}`,
380
- skip: () => initFromExample !== undefined || !protocolInstance.hasContract() || isSubstreams,
381
- initial: initContract,
382
- validate: async (value) => {
383
- if (initFromExample !== undefined || !protocolInstance.hasContract()) {
384
- return true;
385
- }
386
- const protocolContract = protocolInstance.getContract();
387
- if (!protocolContract) {
388
- return 'Contract not found.';
389
- }
390
- // Validate whether the contract is valid
391
- const { valid, error } = (0, validation_1.validateContract)(value, protocolContract);
392
- return valid ? true : error;
393
- },
394
- result: async (value) => {
395
- if (initFromExample !== undefined || isSubstreams) {
396
- return value;
397
- }
398
- const ABI = protocolInstance.getABI();
399
- // Try loading the ABI from Etherscan, if none was provided
400
- if (protocolInstance.hasABIs() && !initAbi) {
401
- try {
402
- if (network === 'poa-core') {
403
- // TODO: this variable is never used anywhere, what happens?
404
- // abiFromBlockScout = await loadAbiFromBlockScout(ABI, network, value)
405
- }
406
- else {
407
- abiFromEtherscan = await (0, abi_1.loadAbiFromEtherscan)(ABI, network, value);
408
- }
409
- }
410
- catch (e) {
411
- // noop
412
- }
413
- }
414
- // If startBlock is not set, try to load it.
415
- if (!initStartBlock) {
416
- try {
417
- // Load startBlock for this contract
418
- initStartBlock = Number(await (0, abi_1.loadStartBlockForContract)(network, value)).toString();
366
+ skip: fromExample !== undefined,
367
+ initial: network || 'mainnet',
368
+ result: (value) => {
369
+ network = value;
370
+ return value;
371
+ },
372
+ },
373
+ // TODO:
374
+ //
375
+ // protocols that don't support contract
376
+ // - arweave
377
+ // - cosmos
378
+ {
379
+ type: 'input',
380
+ name: 'contract',
381
+ message: () => {
382
+ ProtocolContract = protocolInstance.getContract();
383
+ return `Contract ${ProtocolContract.identifierName()}`;
384
+ },
385
+ skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
386
+ initial: contract,
387
+ validate: async (value) => {
388
+ if (fromExample !== undefined || !protocolInstance.hasContract()) {
389
+ return true;
390
+ }
391
+ // Validate whether the contract is valid
392
+ const { valid, error } = (0, validation_1.validateContract)(value, ProtocolContract);
393
+ return valid ? true : error;
394
+ },
395
+ result: async (value) => {
396
+ if (fromExample !== undefined) {
397
+ return value;
398
+ }
399
+ ABI = protocolInstance.getABI();
400
+ // Try loading the ABI from Etherscan, if none was provided
401
+ if (protocolInstance.hasABIs() && !abi) {
402
+ try {
403
+ if (network === 'poa-core') {
404
+ // TODO: this variable is never used anywhere, what happens?
405
+ // abiFromBlockScout = await loadAbiFromBlockScout(ABI, network, value)
419
406
  }
420
- catch (error) {
421
- // noop
407
+ else {
408
+ abiFromEtherscan = await (0, abi_1.loadAbiFromEtherscan)(ABI, network, value);
422
409
  }
423
410
  }
424
- return value;
425
- },
426
- },
427
- ]);
428
- const { abi: abiFromFile } = await gluegun_1.prompt.ask([
429
- {
430
- type: 'input',
431
- name: 'abi',
432
- message: 'ABI file (path)',
433
- initial: initAbi,
434
- skip: () => !protocolInstance.hasABIs() ||
435
- initFromExample !== undefined ||
436
- abiFromEtherscan !== undefined ||
437
- isSubstreams,
438
- validate: async (value) => {
439
- if (initFromExample || abiFromEtherscan || !protocolInstance.hasABIs()) {
440
- return true;
411
+ catch (e) {
412
+ // noop
441
413
  }
442
- const ABI = protocolInstance.getABI();
414
+ }
415
+ // If startBlock is not set, try to load it.
416
+ if (!startBlock) {
443
417
  try {
444
- return loadAbiFromFile(ABI, value);
418
+ // Load startBlock for this contract
419
+ startBlock = Number(await (0, abi_1.loadStartBlockForContract)(network, value)).toString();
445
420
  }
446
- catch (e) {
447
- return e.message;
421
+ catch (error) {
422
+ // noop
448
423
  }
449
- },
424
+ }
425
+ return value;
450
426
  },
451
- ]);
452
- const { startBlock } = await gluegun_1.prompt.ask([
453
- {
454
- type: 'input',
455
- name: 'startBlock',
456
- message: 'Start Block',
457
- initial: initStartBlock || '0',
458
- skip: () => initFromExample !== undefined || isSubstreams,
459
- validate: value => parseInt(value) >= 0,
460
- result(value) {
461
- if (initStartBlock)
462
- return initStartBlock;
463
- return value;
464
- },
427
+ },
428
+ {
429
+ type: 'input',
430
+ name: 'abi',
431
+ message: 'ABI file (path)',
432
+ initial: abi,
433
+ skip: () => !protocolInstance.hasABIs() || fromExample !== undefined || abiFromEtherscan !== undefined,
434
+ validate: async (value) => {
435
+ if (fromExample || abiFromEtherscan || !protocolInstance.hasABIs()) {
436
+ return true;
437
+ }
438
+ try {
439
+ abiFromFile = loadAbiFromFile(ABI, value);
440
+ return true;
441
+ }
442
+ catch (e) {
443
+ return e.message;
444
+ }
465
445
  },
466
- ]);
467
- const { contractName } = await gluegun_1.prompt.ask([
468
- {
469
- type: 'input',
470
- name: 'contractName',
471
- message: 'Contract Name',
472
- initial: initContractName || 'Contract' || isSubstreams,
473
- skip: () => initFromExample !== undefined || !protocolInstance.hasContract(),
474
- validate: value => value && value.length > 0,
446
+ },
447
+ {
448
+ type: 'input',
449
+ name: 'startBlock',
450
+ message: 'Start Block',
451
+ initial: () => startBlock || '0',
452
+ skip: () => fromExample !== undefined,
453
+ validate: (value) => parseInt(value) >= 0,
454
+ result: (value) => {
455
+ startBlock = value;
456
+ return value;
475
457
  },
476
- ]);
477
- const { indexEvents } = await gluegun_1.prompt.ask([
478
- {
479
- type: 'confirm',
480
- name: 'indexEvents',
481
- message: 'Index contract events as entities',
482
- initial: true,
483
- skip: () => !!initIndexEvents || isSubstreams,
458
+ },
459
+ {
460
+ type: 'input',
461
+ name: 'contractName',
462
+ message: 'Contract Name',
463
+ initial: contractName || 'Contract',
464
+ skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
465
+ validate: (value) => value && value.length > 0,
466
+ result: (value) => {
467
+ contractName = value;
468
+ return value;
484
469
  },
485
- ]);
470
+ },
471
+ {
472
+ type: 'confirm',
473
+ name: 'indexEvents',
474
+ message: 'Index contract events as entities',
475
+ initial: true,
476
+ skip: () => !!indexEvents,
477
+ result: (value) => {
478
+ indexEvents = value;
479
+ return value;
480
+ },
481
+ },
482
+ ];
483
+ try {
484
+ const answers = await gluegun_1.prompt.ask(questions);
486
485
  return {
486
+ ...answers,
487
487
  abi: abiFromEtherscan || abiFromFile,
488
488
  protocolInstance,
489
- subgraphName,
490
- directory,
491
- studio: product === 'subgraph-studio',
492
- startBlock,
493
- fromExample: !!initFromExample,
494
- product,
495
- network,
496
- contractName,
497
- contract,
498
- indexEvents,
499
489
  };
500
490
  }
501
491
  catch (e) {
@@ -573,8 +563,7 @@ Subgraph ${subgraphName} created in ${relativeDir}
573
563
 
574
564
  Make sure to visit the documentation on https://thegraph.com/docs/ for further information.`);
575
565
  }
576
- async function initSubgraphFromExample({ protocolInstance, fromExample, allowSimpleName, subgraphName, directory, studio, product, }, { commands, }) {
577
- const isSubstreams = protocolInstance.name === 'substreams';
566
+ async function initSubgraphFromExample({ fromExample, allowSimpleName, subgraphName, directory, studio, product, }, { commands, }) {
578
567
  // Fail if the subgraph name is invalid
579
568
  if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) {
580
569
  process.exitCode = 1;
@@ -667,18 +656,15 @@ async function initSubgraphFromExample({ protocolInstance, fromExample, allowSim
667
656
  this.exit(1);
668
657
  return;
669
658
  }
670
- if (!isSubstreams) {
671
- // Run code-generation
672
- const codegen = await runCodegen(directory, commands.codegen);
673
- if (codegen !== true) {
674
- this.exit(1);
675
- return;
676
- }
659
+ // Run code-generation
660
+ const codegen = await runCodegen(directory, commands.codegen);
661
+ if (codegen !== true) {
662
+ this.exit(1);
663
+ return;
677
664
  }
678
665
  printNextSteps.bind(this)({ subgraphName, directory }, { commands });
679
666
  }
680
667
  async function initSubgraphFromContract({ protocolInstance, allowSimpleName, subgraphName, directory, abi, network, contract, indexEvents, contractName, node, studio, product, startBlock, }, { commands, addContract, }) {
681
- const isSubstreams = protocolInstance.name === 'substreams';
682
668
  // Fail if the subgraph name is invalid
683
669
  if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) {
684
670
  this.exit(1);
@@ -744,17 +730,14 @@ async function initSubgraphFromContract({ protocolInstance, allowSimpleName, sub
744
730
  this.exit(1);
745
731
  return;
746
732
  }
747
- // Substreams we have nothing to install or generate
748
- if (!isSubstreams) {
749
- // Run code-generation
750
- const codegen = await runCodegen(directory, commands.codegen);
751
- if (codegen !== true) {
752
- this.exit(1);
753
- return;
754
- }
755
- while (addContract) {
756
- addContract = await addAnotherContract.bind(this)({ protocolInstance, directory });
757
- }
733
+ // Run code-generation
734
+ const codegen = await runCodegen(directory, commands.codegen);
735
+ if (codegen !== true) {
736
+ this.exit(1);
737
+ return;
738
+ }
739
+ while (addContract) {
740
+ addContract = await addAnotherContract.bind(this)({ protocolInstance, directory });
758
741
  }
759
742
  printNextSteps.bind(this)({ subgraphName, directory }, { commands });
760
743
  }
@@ -88,7 +88,7 @@ class Protocol {
88
88
  });
89
89
  }
90
90
  static availableNetworks() {
91
- return immutable_1.default.fromJS({
91
+ let networks = immutable_1.default.fromJS({
92
92
  arweave: ['arweave-mainnet'],
93
93
  ethereum: [
94
94
  'mainnet',
@@ -135,6 +135,13 @@ class Protocol {
135
135
  ],
136
136
  substreams: ['mainnet'],
137
137
  });
138
+ const allNetworks = [];
139
+ // eslint-disable-next-line unicorn/no-array-for-each
140
+ networks.forEach(value => {
141
+ allNetworks.push(...value);
142
+ });
143
+ networks = networks.set('substreams', immutable_1.default.List(allNetworks));
144
+ return networks;
138
145
  }
139
146
  static normalizeName(name) {
140
147
  return Protocol.availableProtocols().findKey(possibleNames => {
@@ -23,7 +23,6 @@ export default class Scaffold {
23
23
  startBlock?: string;
24
24
  constructor(options: ScaffoldOptions);
25
25
  generatePackageJson(): string;
26
- generatePackageJsonForSubstreams(): string;
27
26
  generateManifest(): string;
28
27
  generateSchema(): string;
29
28
  generateTsConfig(): string;
@@ -38,14 +37,6 @@ export default class Scaffold {
38
37
  [x: string]: string;
39
38
  } | undefined;
40
39
  generate(): {
41
- 'subgraph.yaml': string;
42
- 'schema.graphql': string;
43
- 'package.json': string;
44
- 'tsconfig.json'?: undefined;
45
- src?: undefined;
46
- abis?: undefined;
47
- tests?: undefined;
48
- } | {
49
40
  'package.json': string;
50
41
  'subgraph.yaml': string;
51
42
  'schema.graphql': string;
@@ -51,26 +51,6 @@ class Scaffold {
51
51
  devDependencies: this.protocol.hasEvents() ? { 'matchstick-as': `0.5.0` } : undefined,
52
52
  }), { parser: 'json' });
53
53
  }
54
- generatePackageJsonForSubstreams() {
55
- return prettier_1.default.format(JSON.stringify({
56
- name: (0, subgraph_1.getSubgraphBasename)(String(this.subgraphName)),
57
- license: 'UNLICENSED',
58
- scripts: {
59
- build: 'graph build',
60
- deploy: `graph deploy ` + `--node ${this.node} ` + this.subgraphName,
61
- 'create-local': `graph create --node http://localhost:8020/ ${this.subgraphName}`,
62
- 'remove-local': `graph remove --node http://localhost:8020/ ${this.subgraphName}`,
63
- 'deploy-local': `graph deploy ` +
64
- `--node http://localhost:8020/ ` +
65
- `--ipfs http://localhost:5001 ` +
66
- this.subgraphName,
67
- test: 'graph test',
68
- },
69
- dependencies: {
70
- '@graphprotocol/graph-cli': GRAPH_CLI_VERSION,
71
- },
72
- }), { parser: 'json' });
73
- }
74
54
  generateManifest() {
75
55
  const protocolManifest = this.protocol.getManifestScaffold();
76
56
  return prettier_1.default.format(`
@@ -135,13 +115,6 @@ dataSources:
135
115
  : undefined;
136
116
  }
137
117
  generate() {
138
- if (this.protocol.name === 'substreams') {
139
- return {
140
- 'subgraph.yaml': this.generateManifest(),
141
- 'schema.graphql': this.generateSchema(),
142
- 'package.json': this.generatePackageJsonForSubstreams(),
143
- };
144
- }
145
118
  return {
146
119
  'package.json': this.generatePackageJson(),
147
120
  'subgraph.yaml': this.generateManifest(),
@@ -130,6 +130,7 @@ class TypeGenerator {
130
130
  typescript_1.GENERATED_FILE_NOTE,
131
131
  ...codeGenerator.generateModuleImports(),
132
132
  ...codeGenerator.generateTypes(),
133
+ ...codeGenerator.generateDerivedLoaders(),
133
134
  ].join('\n'), {
134
135
  parser: 'typescript',
135
136
  });
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.49.0-alpha-20230504181204-a99dddd",
2
+ "version": "0.49.0-alpha-20230505103249-d3fbf0b",
3
3
  "commands": {
4
4
  "add": {
5
5
  "id": "add",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphprotocol/graph-cli",
3
- "version": "0.49.0-alpha-20230504181204-a99dddd",
3
+ "version": "0.49.0-alpha-20230505103249-d3fbf0b",
4
4
  "description": "CLI for building for and deploying to The Graph",
5
5
  "license": "(Apache-2.0 OR MIT)",
6
6
  "engines": {
@@ -31,7 +31,7 @@
31
31
  "dockerode": "2.5.8",
32
32
  "fs-extra": "9.1.0",
33
33
  "glob": "9.3.5",
34
- "gluegun": "5.1.2",
34
+ "gluegun": "https://github.com/edgeandnode/gluegun#v4.3.1-pin-colors-dep",
35
35
  "graphql": "15.5.0",
36
36
  "immutable": "4.2.1",
37
37
  "ipfs-http-client": "55.0.0",