create-ponder 0.15.18 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
5
- import path4 from "node:path";
4
+ import { mkdirSync, writeFileSync } from "node:fs";
5
+ import path2 from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { cac } from "cac";
8
8
  import cpy from "cpy";
9
9
  import { execa } from "execa";
10
10
  import fs from "fs-extra";
11
11
  import { oraPromise } from "ora";
12
- import pico5 from "picocolors";
13
- import prettier3 from "prettier";
12
+ import pico4 from "picocolors";
13
+ import prettier from "prettier";
14
14
  import { default as prompts } from "prompts";
15
15
 
16
16
  // package.json
17
17
  var package_default = {
18
18
  name: "create-ponder",
19
- version: "0.15.18",
19
+ version: "0.16.0",
20
20
  type: "module",
21
21
  description: "A CLI tool to create Ponder apps",
22
22
  license: "MIT",
@@ -32,6 +32,7 @@ var package_default = {
32
32
  scripts: {
33
33
  build: "tsup",
34
34
  test: "vitest run",
35
+ "test:bun": "bun test",
35
36
  typecheck: "tsc --noEmit"
36
37
  },
37
38
  dependencies: {
@@ -64,274 +65,8 @@ var package_default = {
64
65
  }
65
66
  };
66
67
 
67
- // src/etherscan.ts
68
- import { mkdirSync, writeFileSync } from "node:fs";
69
- import path from "node:path";
70
-
71
- // src/helpers/wait.ts
72
- var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
73
-
74
- // src/etherscan.ts
75
- import pico from "picocolors";
76
- import prettier from "prettier";
77
- import * as chains from "viem/chains";
78
- var chainExplorerByHostname = {};
79
- for (const [name, chain] of Object.entries(chains)) {
80
- for (const explorer of Object.values(chain.blockExplorers ?? {})) {
81
- const hostname = new URL(explorer.url).hostname;
82
- chainExplorerByHostname[hostname] = {
83
- name,
84
- id: chain.id,
85
- explorer
86
- };
87
- }
88
- }
89
- var fromEtherscan = async ({
90
- rootDir,
91
- etherscanLink,
92
- etherscanApiKey
93
- }) => {
94
- const warnings = [];
95
- const apiKey = etherscanApiKey || process.env.ETHERSCAN_API_KEY;
96
- const explorerUrl = new URL(etherscanLink);
97
- const chainExplorer = chainExplorerByHostname[explorerUrl.hostname];
98
- if (!chainExplorer)
99
- throw new Error(
100
- `Block explorer (${explorerUrl.hostname}) is not present in viem/chains.`
101
- );
102
- const name = chainExplorer.name;
103
- const chainId = chainExplorer.id;
104
- const apiUrl = chainExplorer.explorer.apiUrl;
105
- if (!apiUrl)
106
- throw new Error(
107
- `${pico.red("\u2717")} Block explorer (${explorerUrl.hostname}) does not have a API URL registered in viem/chains.`
108
- );
109
- const pathComponents = explorerUrl.pathname.slice(1).split("/");
110
- const contractAddress = pathComponents[1];
111
- if (pathComponents[0] !== "address" || !(typeof contractAddress === "string") || !contractAddress.startsWith("0x")) {
112
- throw new Error(
113
- `${pico.red("\u2717")} Invalid block explorer URL (${explorerUrl.href}). Expected path "/address/<contract-address>".`
114
- );
115
- }
116
- const abiResult = await getContractAbiAndName(
117
- contractAddress,
118
- apiUrl,
119
- apiKey
120
- );
121
- warnings.push(...abiResult.warnings);
122
- const baseAbi = abiResult.abi;
123
- let contractName = abiResult.contractName;
124
- const abis = [
125
- { abi: baseAbi, contractName }
126
- ];
127
- let blockNumber = void 0;
128
- try {
129
- if (!apiKey)
130
- await wait(5e3);
131
- const txHash = await getContractCreationTxn(
132
- contractAddress,
133
- apiUrl,
134
- apiKey
135
- );
136
- if (!apiKey)
137
- await wait(5e3);
138
- const contractCreationBlockNumber = await getTxBlockNumber(
139
- txHash,
140
- apiUrl,
141
- apiKey
142
- );
143
- blockNumber = contractCreationBlockNumber;
144
- } catch (e) {
145
- const error = e;
146
- warnings.push(
147
- `Unable to fetch contract deployment block number from block explorer. Error: ${error.message}`
148
- );
149
- }
150
- if (baseAbi.find(
151
- (item) => item.type === "event" && item.name === "Upgraded" && item.inputs[0].name === "implementation"
152
- )) {
153
- if (!apiKey)
154
- await wait(5e3);
155
- const { implAddresses } = await getProxyImplementationAddresses({
156
- contractAddress,
157
- apiUrl,
158
- fromBlock: blockNumber,
159
- apiKey
160
- });
161
- for (const implAddress of implAddresses) {
162
- if (!apiKey)
163
- await wait(5e3);
164
- const abiResult2 = await getContractAbiAndName(
165
- implAddress,
166
- apiUrl,
167
- apiKey
168
- );
169
- warnings.push(...abiResult2.warnings);
170
- abis.push({
171
- abi: abiResult2.abi,
172
- contractName: `${abiResult2.contractName}_${implAddress.slice(0, 6)}`
173
- });
174
- contractName = abiResult2.contractName;
175
- }
176
- }
177
- mkdirSync(path.join(rootDir, "abis"), { recursive: true });
178
- mkdirSync(path.join(rootDir, "src"), { recursive: true });
179
- let abiConfig;
180
- for (const { abi, contractName: contractName2 } of abis) {
181
- const abiRelativePath = `./abis/${contractName2}Abi.ts`;
182
- const abiAbsolutePath = path.join(
183
- path.resolve(".", rootDir),
184
- abiRelativePath
185
- );
186
- writeFileSync(
187
- abiAbsolutePath,
188
- await prettier.format(
189
- `export const ${contractName2}Abi = ${JSON.stringify(abi)} as const`,
190
- {
191
- parser: "typescript"
192
- }
193
- )
194
- );
195
- if (abis.length === 1) {
196
- abiConfig = {
197
- abi,
198
- dir: abiRelativePath,
199
- name: `${contractName2}Abi`
200
- };
201
- } else {
202
- if (abiConfig === void 0) {
203
- abiConfig = [];
204
- }
205
- abiConfig.push({
206
- abi,
207
- name: `${contractName2}Abi`,
208
- dir: abiRelativePath
209
- });
210
- }
211
- }
212
- const config = {
213
- chains: {
214
- [name]: {
215
- id: chainId,
216
- rpc: `http(process.env.PONDER_RPC_URL_${chainId})`
217
- }
218
- },
219
- contracts: {
220
- [contractName]: {
221
- abi: abiConfig,
222
- address: contractAddress,
223
- chain: name,
224
- startBlock: blockNumber ?? void 0
225
- }
226
- }
227
- };
228
- return { config, warnings };
229
- };
230
- var fetchEtherscan = async (url) => {
231
- const maxRetries = 5;
232
- let retryCount = 0;
233
- while (retryCount <= maxRetries) {
234
- try {
235
- const response = await fetch(url);
236
- const data = await response.json();
237
- return data;
238
- } catch (error) {
239
- retryCount++;
240
- if (retryCount > maxRetries) {
241
- throw new Error(`Max retries reached: ${error.message}`);
242
- }
243
- await new Promise((resolve) => setTimeout(resolve, 100));
244
- }
245
- }
246
- };
247
- var getContractCreationTxn = async (contractAddress, apiUrl, apiKey) => {
248
- const searchParams = new URLSearchParams({
249
- module: "contract",
250
- action: "getcontractcreation",
251
- contractaddresses: contractAddress
252
- });
253
- if (apiKey)
254
- searchParams.append("apikey", apiKey);
255
- const data = await fetchEtherscan(`${apiUrl}?${searchParams.toString()}`);
256
- return data.result[0].txHash;
257
- };
258
- var getTxBlockNumber = async (txHash, apiUrl, apiKey) => {
259
- const searchParams = new URLSearchParams({
260
- module: "proxy",
261
- action: "eth_getTransactionByHash",
262
- txhash: txHash
263
- });
264
- if (apiKey)
265
- searchParams.append("apikey", apiKey);
266
- const data = await fetchEtherscan(`${apiUrl}?${searchParams.toString()}`);
267
- const hexBlockNumber = data.result.blockNumber;
268
- return Number.parseInt(hexBlockNumber.slice(2), 16);
269
- };
270
- var getContractAbiAndName = async (contractAddress, apiUrl, apiKey) => {
271
- const searchParams = new URLSearchParams({
272
- module: "contract",
273
- action: "getsourcecode",
274
- address: contractAddress
275
- });
276
- if (apiKey)
277
- searchParams.append("apikey", apiKey);
278
- const warnings = [];
279
- let abi;
280
- let contractName;
281
- try {
282
- const data = await fetchEtherscan(`${apiUrl}?${searchParams.toString()}`);
283
- const rawAbi = data.result[0].ABI;
284
- if (rawAbi === "Contract source code not verified") {
285
- warnings.push(
286
- `Contract ${contractAddress} is unverified or has an empty ABI.`
287
- );
288
- abi = [];
289
- } else {
290
- abi = JSON.parse(rawAbi);
291
- }
292
- contractName = data.result[0].ContractName ?? "";
293
- if (contractName === "")
294
- contractName = "UnverifiedContract";
295
- } catch (e) {
296
- const error = e;
297
- warnings.push(
298
- `Failed to fetch ABI for contract ${contractAddress}. Marking as unverified. Error: ${error.message}`
299
- );
300
- abi = [];
301
- contractName = "UnverifiedContract";
302
- }
303
- return { abi, contractName, warnings };
304
- };
305
- var getProxyImplementationAddresses = async ({
306
- contractAddress,
307
- apiUrl,
308
- fromBlock,
309
- apiKey
310
- }) => {
311
- const searchParams = new URLSearchParams({
312
- module: "logs",
313
- action: "getLogs",
314
- address: contractAddress,
315
- fromBlock: fromBlock ? String(fromBlock) : "0",
316
- toBlock: "latest",
317
- topic0: "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b"
318
- });
319
- if (apiKey)
320
- searchParams.append("apikey", apiKey);
321
- const data = await fetchEtherscan(`${apiUrl}?${searchParams.toString()}`);
322
- const logs = data.result;
323
- const implAddresses = logs.map((log3) => {
324
- if (log3.topics[0] && log3.topics[1]) {
325
- return `0x${log3.topics[1].slice(26)}`;
326
- } else {
327
- return `0x${log3.data.slice(26)}`;
328
- }
329
- });
330
- return { implAddresses };
331
- };
332
-
333
68
  // src/helpers/getPackageManager.ts
334
- import pico2 from "picocolors";
69
+ import pico from "picocolors";
335
70
  var getPackageManager = ({
336
71
  options
337
72
  }) => {
@@ -356,7 +91,7 @@ var getPackageManager = ({
356
91
  if (userAgent.includes("yarn"))
357
92
  return "yarn";
358
93
  }
359
- throw new Error(pico2.red("Undetectable package manager"));
94
+ throw new Error(pico.red("Undetectable package manager"));
360
95
  };
361
96
 
362
97
  // src/helpers/mergeAbis.ts
@@ -375,7 +110,7 @@ var mergeAbis = (abis) => {
375
110
  };
376
111
 
377
112
  // src/helpers/notifyUpdate.ts
378
- import pico3 from "picocolors";
113
+ import pico2 from "picocolors";
379
114
  import checkForUpdate from "update-check";
380
115
  var log = console.log;
381
116
  async function notifyUpdate({ options }) {
@@ -385,11 +120,11 @@ async function notifyUpdate({ options }) {
385
120
  const packageManager = getPackageManager({ options });
386
121
  const updateMessage = packageManager === "bun" ? "bun install --global create-ponder" : packageManager === "pnpm" ? "pnpm add -g create-ponder" : packageManager === "npm" ? "npm install -g create-ponder" : "yarn global add create-ponder";
387
122
  log(
388
- pico3.bold(
389
- `${pico3.yellow(
123
+ pico2.bold(
124
+ `${pico2.yellow(
390
125
  "A new version of `create-ponder` is available!"
391
126
  )}
392
- You can update by running: ${pico3.cyan(updateMessage)}
127
+ You can update by running: ${pico2.cyan(updateMessage)}
393
128
  `
394
129
  )
395
130
  );
@@ -400,9 +135,9 @@ You can update by running: ${pico3.cyan(updateMessage)}
400
135
  }
401
136
 
402
137
  // src/helpers/validate.ts
403
- import path2 from "node:path";
138
+ import path from "node:path";
404
139
  import { pathExists } from "fs-extra";
405
- import pico4 from "picocolors";
140
+ import pico3 from "picocolors";
406
141
  import validatePackageName from "validate-npm-package-name";
407
142
  async function validateProjectName({
408
143
  projectName
@@ -429,7 +164,7 @@ async function validateProjectPath({
429
164
  if (await pathExists(projectPath))
430
165
  return {
431
166
  valid: false,
432
- message: `\u{1F648} the directory "${path2.relative(process.cwd(), projectPath)}" already exists.`,
167
+ message: `\u{1F648} the directory "${path.relative(process.cwd(), projectPath)}" already exists.`,
433
168
  problems: "\u{1F449} choose another name or delete the directory."
434
169
  };
435
170
  return {
@@ -458,179 +193,14 @@ async function validateTemplateName({
458
193
  var ValidationError = class extends Error {
459
194
  name = "ValidationError";
460
195
  constructor(validation) {
461
- super([pico4.red(validation.message), validation.problems].join("\n"));
196
+ super([pico3.red(validation.message), validation.problems].join("\n"));
462
197
  }
463
198
  };
464
199
 
465
- // src/subgraph.ts
466
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
467
- import path3 from "node:path";
468
- import prettier2 from "prettier";
469
- import { parse } from "yaml";
470
-
471
- // src/helpers/getGraphProtocolChainId.ts
472
- var chainIdByGraphNetwork = {
473
- mainnet: 1,
474
- kovan: 42,
475
- rinkeby: 4,
476
- ropsten: 3,
477
- goerli: 5,
478
- sepolia: 11155111,
479
- "poa-core": 99,
480
- "poa-sokol": 77,
481
- xdai: 100,
482
- matic: 137,
483
- mumbai: 80001,
484
- fantom: 250,
485
- "fantom-testnet": 4002,
486
- bsc: 56,
487
- avalanche: 43114,
488
- fuji: 43113,
489
- celo: 42220,
490
- "celo-alfajores": 44787,
491
- fuse: 122,
492
- moonbeam: 1284,
493
- moonriver: 1285,
494
- base: 8453,
495
- "base-sepolia": 84532,
496
- "arbitrum-one": 42161,
497
- "arbitrum-sepolia": 421614,
498
- optimism: 10,
499
- "optimism-sepolia": 11155420,
500
- aurora: 1313161554,
501
- "aurora-testnet": 1313161555
502
- };
503
- var getGraphProtocolChainId = (networkName) => {
504
- return chainIdByGraphNetwork[networkName] ?? 0;
505
- };
506
- var subgraphYamlFileNames = ["subgraph.yaml"].concat(
507
- Object.keys(chainIdByGraphNetwork).map((n) => `subgraph-${n}.yaml`)
508
- );
509
-
510
- // src/helpers/validateGraphProtocolSource.ts
511
- var validateGraphProtocolSource = (source) => {
512
- return source;
513
- };
514
-
515
- // src/subgraph.ts
516
- var subgraphProviders = [
517
- {
518
- id: "thegraph",
519
- name: "The Graph",
520
- // Used to be https://ipfs.network.thegraph.com/api/v0/cat?arg=${cid}
521
- // Also used to accept GET requests for some reason
522
- fetchIpfs: async (cid) => {
523
- const response = await fetch(
524
- `https://api.thegraph.com/ipfs/api/v0/cat?arg=${cid}`,
525
- { method: "POST" }
526
- );
527
- return await response.text();
528
- }
529
- },
530
- {
531
- id: "satsuma",
532
- name: "Alchemy Subgraph (Satsuma)",
533
- fetchIpfs: async (cid) => {
534
- const response = await fetch(`https://ipfs.satsuma.xyz/ipfs/${cid}`);
535
- return await response.text();
536
- }
537
- }
538
- ];
539
- var fromSubgraphId = async ({
540
- rootDir,
541
- subgraphId,
542
- subgraphProvider
543
- }) => {
544
- const provider = subgraphProviders.find((p) => p.id === subgraphProvider);
545
- if (!provider)
546
- throw new Error(`Unknown subgraph provider: ${subgraphProvider}`);
547
- const manifestRaw = await provider.fetchIpfs(subgraphId);
548
- const manifest = parse(manifestRaw);
549
- const contracts = {};
550
- manifest.dataSources.forEach((d) => {
551
- contracts[d.name] = {
552
- chain: d.network,
553
- address: d.source.address,
554
- startBlock: d.source.startBlock
555
- };
556
- });
557
- const dataSources = manifest.dataSources;
558
- mkdirSync2(path3.join(rootDir, "abis"), { recursive: true });
559
- mkdirSync2(path3.join(rootDir, "src"), { recursive: true });
560
- const abiFiles = dataSources.flatMap((source) => validateGraphProtocolSource(source).mapping.abis).filter(
561
- (source, idx, arr) => arr.findIndex((s) => s.name === source.name) === idx
562
- );
563
- const abis = {};
564
- await Promise.all(
565
- abiFiles.map(async (abi) => {
566
- const abiContent = await provider.fetchIpfs(abi.file["/"].slice(6));
567
- const abiPath = path3.join(rootDir, `./abis/${abi.name}Abi.ts`);
568
- writeFileSync2(
569
- abiPath,
570
- await prettier2.format(
571
- `export const ${abi.name}Abi = ${abiContent} as const`,
572
- {
573
- parser: "typescript"
574
- }
575
- )
576
- );
577
- abis[abi.name] = JSON.parse(abiContent);
578
- })
579
- );
580
- const ponderContracts = dataSources.map((sourceInvalid) => {
581
- const source = validateGraphProtocolSource(sourceInvalid);
582
- const chain = source.network || "mainnet";
583
- const abiRelativePath = `./abis/${source.source.abi}Abi.ts`;
584
- return {
585
- name: source.name,
586
- chain,
587
- address: source.source.address,
588
- abi: {
589
- abi: abis[source.source.abi],
590
- dir: abiRelativePath,
591
- name: `${source.source.abi}Abi`
592
- },
593
- startBlock: source.source.startBlock
594
- };
595
- });
596
- const contractsObject = {};
597
- const chainsObject = {};
598
- ponderContracts.forEach((pc) => {
599
- const chainId = getGraphProtocolChainId(pc.chain);
600
- contractsObject[pc.name] = pc;
601
- chainsObject[pc.chain] = {
602
- id: chainId,
603
- rpc: `process.env.PONDER_RPC_URL_${chainId}!`
604
- };
605
- contractsObject[pc.name].name = void 0;
606
- });
607
- const config = {
608
- chains: chainsObject,
609
- contracts: contractsObject
610
- };
611
- const warnings = [];
612
- if (manifest.templates?.length > 0) {
613
- warnings.push(
614
- "Factory contract detected. Please see the factory contract documentation for more details: https://ponder.sh/docs/indexing/read-contracts#factory-contracts"
615
- );
616
- }
617
- return { config, warnings };
618
- };
619
-
620
200
  // src/index.ts
621
201
  var log2 = console.log;
622
202
  var templates = [
623
203
  { id: "empty", title: "Default", description: "A blank-slate Ponder app" },
624
- {
625
- id: "etherscan",
626
- title: "Etherscan contract link",
627
- description: "Create from an Etherscan contract link"
628
- },
629
- {
630
- id: "subgraph",
631
- title: "Subgraph ID",
632
- description: "Create from a deployed subgraph"
633
- },
634
204
  {
635
205
  id: "feature-factory",
636
206
  title: "Feature - Factory contract",
@@ -716,13 +286,13 @@ async function run({
716
286
  const warnings = [];
717
287
  log2();
718
288
  log2(
719
- `Welcome to ${pico5.bold(
720
- pico5.blue("create-ponder")
289
+ `Welcome to ${pico4.bold(
290
+ pico4.blue("create-ponder")
721
291
  )} \u2013 the quickest way to get started with Ponder!`
722
292
  );
723
293
  log2();
724
294
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
725
- const templatesPath = path4.join(__dirname, "..", "templates");
295
+ const templatesPath = path2.join(__dirname, "..", "templates");
726
296
  let templateId = options.template || options.t;
727
297
  let templateValidation = await validateTemplateName({
728
298
  isNameRequired: false,
@@ -735,14 +305,14 @@ async function run({
735
305
  let projectPath;
736
306
  if (args[0]) {
737
307
  projectPath = args[0].trim();
738
- if (!path4.isAbsolute(projectPath))
739
- projectPath = path4.resolve(projectPath);
740
- const splitPath = projectPath.split(path4.sep);
308
+ if (!path2.isAbsolute(projectPath))
309
+ projectPath = path2.resolve(projectPath);
310
+ const splitPath = projectPath.split(path2.sep);
741
311
  projectName = splitPath[splitPath.length - 1]?.trim() || "";
742
312
  const nameValidation = await validateProjectName({ projectName });
743
313
  if (!nameValidation.valid)
744
314
  throw new ValidationError(nameValidation);
745
- log2(pico5.green("\u2714"), pico5.bold("Using project name:"), projectName);
315
+ log2(pico4.green("\u2714"), pico4.bold("Using project name:"), projectName);
746
316
  } else {
747
317
  const res = await prompts({
748
318
  initial: "my-app",
@@ -757,12 +327,12 @@ async function run({
757
327
  }
758
328
  });
759
329
  projectName = res.projectName?.trim();
760
- projectPath = path4.resolve(projectName);
330
+ projectPath = path2.resolve(projectName);
761
331
  }
762
332
  const pathValidation = await validateProjectPath({ projectPath });
763
333
  if (!pathValidation.valid)
764
334
  throw new ValidationError(pathValidation);
765
- mkdirSync3(projectPath, { recursive: true });
335
+ mkdirSync(projectPath, { recursive: true });
766
336
  if (options.etherscan && !templateId)
767
337
  templateId = "etherscan";
768
338
  if (options.subgraph && !templateId)
@@ -788,83 +358,9 @@ async function run({
788
358
  if (!templateValidation.valid)
789
359
  throw new ValidationError(templateValidation);
790
360
  let config;
791
- let url = options.etherscan;
792
- if (templateMeta.id === "etherscan") {
793
- if (!url) {
794
- const result = await prompts({
795
- type: "text",
796
- name: "url",
797
- message: "Enter a block explorer contract url",
798
- initial: "https://etherscan.io/address/0x97..."
799
- });
800
- url = result.url;
801
- }
802
- }
803
- let subgraph = options.subgraph;
804
- let subgraphProvider = options.subgraphProvider;
805
- if (templateMeta.id === "subgraph") {
806
- if (subgraphProvider === void 0) {
807
- const result = await prompts({
808
- name: "subgraphProvider",
809
- message: "Which provider is the subgraph deployed to?",
810
- type: "select",
811
- choices: subgraphProviders.map(({ id, name }) => ({
812
- title: name,
813
- value: id
814
- }))
815
- });
816
- subgraphProvider = result.subgraphProvider;
817
- }
818
- if (!subgraph) {
819
- const result = await prompts({
820
- type: "text",
821
- name: "id",
822
- message: "Enter a subgraph Deployment ID",
823
- initial: "Qmb3hd2hYd2nWFgcmRswykF1dUBSrDUrinYCgN1dmE1tNy"
824
- });
825
- subgraph = result.id;
826
- }
827
- if (!subgraph) {
828
- log2(pico5.red("No subgraph Deployment ID provided."));
829
- process.exit(0);
830
- }
831
- }
832
361
  log2();
833
- if (templateMeta.id === "etherscan") {
834
- const host = new URL(url).host;
835
- const result = await oraPromise(
836
- fromEtherscan({
837
- rootDir: projectPath,
838
- etherscanLink: url,
839
- etherscanApiKey: options.etherscanApiKey
840
- }),
841
- {
842
- text: `Fetching contract metadata from ${pico5.bold(host)}. This may take a few seconds.`,
843
- failText: "Failed to fetch contract metadata.",
844
- successText: `Fetched contract metadata from ${pico5.bold(host)}.`
845
- }
846
- );
847
- config = result.config;
848
- warnings.push(...result.warnings);
849
- }
850
- if (templateMeta.id === "subgraph") {
851
- const result = await oraPromise(
852
- fromSubgraphId({
853
- rootDir: projectPath,
854
- subgraphId: subgraph,
855
- subgraphProvider
856
- }),
857
- {
858
- text: "Fetching subgraph metadata. This may take a few seconds.",
859
- failText: "Failed to fetch subgraph metadata.",
860
- successText: `Fetched subgraph metadata for ${pico5.bold(subgraph)}.`
861
- }
862
- );
863
- config = result.config;
864
- warnings.push(...result.warnings);
865
- }
866
- const templatePath = path4.join(templatesPath, templateMeta.id);
867
- await cpy(path4.join(templatePath, "**", "*"), projectPath, {
362
+ const templatePath = path2.join(templatesPath, templateMeta.id);
363
+ await cpy(path2.join(templatePath, "**", "*"), projectPath, {
868
364
  rename: (name) => name.replace(/^_dot_/, ".")
869
365
  });
870
366
  if (config) {
@@ -896,9 +392,9 @@ async function run({
896
392
  ).replaceAll(/"abi":"(.*?)"/g, "abi:$1")},
897
393
  });
898
394
  `;
899
- writeFileSync3(
900
- path4.join(projectPath, "ponder.config.ts"),
901
- await prettier3.format(configContent, { parser: "typescript" })
395
+ writeFileSync(
396
+ path2.join(projectPath, "ponder.config.ts"),
397
+ await prettier.format(configContent, { parser: "typescript" })
902
398
  );
903
399
  for (const [name, contract] of Object.entries(config.contracts)) {
904
400
  const abi = Array.isArray(contract.abi) ? mergeAbis(contract.abi.map((a) => a.abi)) : contract.abi.abi;
@@ -916,20 +412,23 @@ async function run({
916
412
  })`
917
413
  ).join("\n")}
918
414
  `;
919
- writeFileSync3(
920
- path4.join(projectPath, "src", `${name}.ts`),
921
- await prettier3.format(indexingFunctionFileContents, {
415
+ writeFileSync(
416
+ path2.join(projectPath, "src", `${name}.ts`),
417
+ await prettier.format(indexingFunctionFileContents, {
922
418
  parser: "typescript"
923
419
  })
924
420
  );
925
421
  }
926
422
  }
927
- const packageJson = await fs.readJSON(path4.join(projectPath, "package.json"));
423
+ const packageJson = await fs.readJSON(path2.join(projectPath, "package.json"));
928
424
  packageJson.name = projectName;
929
425
  packageJson.dependencies.ponder = `^${package_default.version}`;
930
426
  packageJson.devDependencies["eslint-config-ponder"] = `^${package_default.version}`;
427
+ if ("bun" in process.versions) {
428
+ packageJson.scripts = addBunFlagToScripts(packageJson.scripts ?? {});
429
+ }
931
430
  await fs.writeFile(
932
- path4.join(projectPath, "package.json"),
431
+ path2.join(projectPath, "package.json"),
933
432
  JSON.stringify(packageJson, null, 2)
934
433
  );
935
434
  const packageManager = getPackageManager({ options });
@@ -951,9 +450,9 @@ async function run({
951
450
  }
952
451
  }),
953
452
  {
954
- text: `Installing packages with ${pico5.bold(packageManager)}. This may take a few seconds.`,
453
+ text: `Installing packages with ${pico4.bold(packageManager)}. This may take a few seconds.`,
955
454
  failText: "Failed to install packages.",
956
- successText: `Installed packages with ${pico5.bold(packageManager)}.`
455
+ successText: `Installed packages with ${pico4.bold(packageManager)}.`
957
456
  }
958
457
  );
959
458
  }
@@ -982,22 +481,22 @@ async function run({
982
481
  }
983
482
  log2();
984
483
  for (const warning of warnings) {
985
- log2(`${pico5.yellow("\u26A0")} ${warning}`);
484
+ log2(`${pico4.yellow("\u26A0")} ${warning}`);
986
485
  }
987
486
  log2();
988
487
  log2("\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015");
989
488
  log2();
990
489
  log2(
991
- `${pico5.green("Success!")} Created ${pico5.bold(projectName)} at ${pico5.green(
992
- path4.resolve(projectPath)
490
+ `${pico4.green("Success!")} Created ${pico4.bold(projectName)} at ${pico4.green(
491
+ path2.resolve(projectPath)
993
492
  )}`
994
493
  );
995
494
  log2();
996
495
  log2(
997
- `To start your app, run ${pico5.bold(
998
- pico5.cyan(`cd ${path4.relative(process.cwd(), projectPath)}`)
999
- )} and then ${pico5.bold(
1000
- pico5.cyan(
496
+ `To start your app, run ${pico4.bold(
497
+ pico4.cyan(`cd ${path2.relative(process.cwd(), projectPath)}`)
498
+ )} and then ${pico4.bold(
499
+ pico4.cyan(
1001
500
  `${packageManager}${packageManager === "npm" || packageManager === "bun" ? " run" : ""} dev`
1002
501
  )
1003
502
  )}`
@@ -1006,16 +505,20 @@ async function run({
1006
505
  log2("\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015");
1007
506
  log2();
1008
507
  }
508
+ function addBunFlagToScripts(scripts) {
509
+ const ret = {};
510
+ for (const [k, v] of Object.entries(scripts)) {
511
+ if (v.startsWith("ponder"))
512
+ ret[k] = `bun --bun ${v}`;
513
+ else
514
+ ret[k] = v;
515
+ }
516
+ return ret;
517
+ }
1009
518
  (async () => {
1010
- const cli = cac(package_default.name).version(package_default.version).usage(`${pico5.green("<directory>")} [options]`).option(
519
+ const cli = cac(package_default.name).version(package_default.version).usage(`${pico4.green("<directory>")} [options]`).option(
1011
520
  "-t, --template [id]",
1012
521
  `Use a template. Options: ${templates.map(({ id }) => id).join(", ")}`
1013
- ).option("--etherscan [url]", "Use the Etherscan template").option(
1014
- "--etherscan-api-key [key]",
1015
- "Etherscan API key for Etherscan template"
1016
- ).option("--subgraph [id]", "Use the subgraph template").option(
1017
- "--subgraph-provider [provider]",
1018
- `Specify the subgraph provider. Options: ${subgraphProviders.map(({ id }) => id).join(", ")}`
1019
522
  ).option("--npm", "Use npm as your package manager").option("--pnpm", "Use pnpm as your package manager").option("--yarn", "Use yarn as your package manager").option("--skip-git", "Skip initializing a git repository").option("--skip-install", "Skip installing packages").help();
1020
523
  const _nodeVersion = process.version.split(".");
1021
524
  const nodeVersion = [
@@ -1025,7 +528,7 @@ async function run({
1025
528
  ];
1026
529
  if (nodeVersion[0] < 18 || nodeVersion[0] === 18 && nodeVersion[1] < 14)
1027
530
  throw new Error(
1028
- pico5.red(
531
+ pico4.red(
1029
532
  `Node version:${process.version} does not meet the >=18.14 requirement`
1030
533
  )
1031
534
  );
@@ -1036,7 +539,7 @@ async function run({
1036
539
  await notifyUpdate({ options });
1037
540
  } catch (error) {
1038
541
  log2(
1039
- error instanceof ValidationError ? error.message : pico5.red(error.message)
542
+ error instanceof ValidationError ? error.message : pico4.red(error.message)
1040
543
  );
1041
544
  log2();
1042
545
  await notifyUpdate({ options });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ponder",
3
- "version": "0.15.18",
3
+ "version": "0.16.0",
4
4
  "type": "module",
5
5
  "description": "A CLI tool to create Ponder apps",
6
6
  "license": "MIT",
@@ -47,6 +47,7 @@
47
47
  "scripts": {
48
48
  "build": "tsup",
49
49
  "test": "vitest run",
50
+ "test:bun": "bun test",
50
51
  "typecheck": "tsc --noEmit"
51
52
  }
52
53
  }
@@ -1,5 +0,0 @@
1
- # Mainnet RPC URL used for fetching blockchain data. Alchemy is recommended.
2
- PONDER_RPC_URL_1=
3
-
4
- # (Optional) Postgres database URL. If not provided, SQLite will be used.
5
- DATABASE_URL=
@@ -1,3 +0,0 @@
1
- {
2
- "extends": "ponder"
3
- }
@@ -1,18 +0,0 @@
1
- # Dependencies
2
- /node_modules
3
-
4
- # Debug
5
- npm-debug.log*
6
- yarn-debug.log*
7
- yarn-error.log*
8
- .pnpm-debug.log*
9
-
10
- # Misc
11
- .DS_Store
12
-
13
- # Env files
14
- .env*.local
15
-
16
- # Ponder
17
- /generated/
18
- /.ponder/
@@ -1,28 +0,0 @@
1
- {
2
- "name": "ponder-etherscan",
3
- "version": "0.0.1",
4
- "private": true,
5
- "type": "module",
6
- "scripts": {
7
- "dev": "ponder dev",
8
- "start": "ponder start",
9
- "db": "ponder db",
10
- "codegen": "ponder codegen",
11
- "lint": "eslint .",
12
- "typecheck": "tsc"
13
- },
14
- "dependencies": {
15
- "ponder": "^0.0.95",
16
- "hono": "^4.5.0",
17
- "viem": "^2.21.3"
18
- },
19
- "devDependencies": {
20
- "@types/node": "^20.9.0",
21
- "eslint": "^8.53.0",
22
- "eslint-config-ponder": "^0.0.95",
23
- "typescript": "^5.2.2"
24
- },
25
- "engines": {
26
- "node": ">=18.14"
27
- }
28
- }
@@ -1,15 +0,0 @@
1
- /// <reference types="ponder/virtual" />
2
-
3
- declare module "ponder:internal" {
4
- const config: typeof import("./ponder.config.ts");
5
- const schema: typeof import("./ponder.schema.ts");
6
- }
7
-
8
- declare module "ponder:schema" {
9
- export * from "./ponder.schema.ts";
10
- }
11
-
12
- // This file enables type checking and editor autocomplete for this Ponder project.
13
- // After upgrading, you may find that changes have been made to this file.
14
- // If this happens, please commit the changes. Do not manually edit this file.
15
- // See https://ponder.sh/docs/requirements#typescript for more information.
@@ -1,6 +0,0 @@
1
- import { onchainTable } from "ponder";
2
-
3
- export const example = onchainTable("example", (t) => ({
4
- id: t.text().primaryKey(),
5
- name: t.text(),
6
- }));
@@ -1,26 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Type checking
4
- "strict": true,
5
- "noUncheckedIndexedAccess": true,
6
-
7
- // Interop constraints
8
- "verbatimModuleSyntax": false,
9
- "esModuleInterop": true,
10
- "isolatedModules": true,
11
- "allowSyntheticDefaultImports": true,
12
- "resolveJsonModule": true,
13
-
14
- // Language and environment
15
- "moduleResolution": "bundler",
16
- "module": "ESNext",
17
- "noEmit": true,
18
- "lib": ["ES2022"],
19
- "target": "ES2022",
20
-
21
- // Skip type checking for node modules
22
- "skipLibCheck": true
23
- },
24
- "include": ["./**/*.ts"],
25
- "exclude": ["node_modules"]
26
- }
@@ -1,5 +0,0 @@
1
- # Mainnet RPC URL used for fetching blockchain data. Alchemy is recommended.
2
- PONDER_RPC_URL_1=
3
-
4
- # (Optional) Postgres database URL. If not provided, SQLite will be used.
5
- DATABASE_URL=
@@ -1,3 +0,0 @@
1
- {
2
- "extends": "ponder"
3
- }
@@ -1,18 +0,0 @@
1
- # Dependencies
2
- /node_modules
3
-
4
- # Debug
5
- npm-debug.log*
6
- yarn-debug.log*
7
- yarn-error.log*
8
- .pnpm-debug.log*
9
-
10
- # Misc
11
- .DS_Store
12
-
13
- # Env files
14
- .env*.local
15
-
16
- # Ponder
17
- /generated/
18
- /.ponder/
@@ -1,28 +0,0 @@
1
- {
2
- "name": "ponder-etherscan",
3
- "version": "0.0.1",
4
- "private": true,
5
- "type": "module",
6
- "scripts": {
7
- "dev": "ponder dev",
8
- "start": "ponder start",
9
- "db": "ponder db",
10
- "codegen": "ponder codegen",
11
- "lint": "eslint .",
12
- "typecheck": "tsc"
13
- },
14
- "dependencies": {
15
- "ponder": "^0.0.95",
16
- "hono": "^4.5.0",
17
- "viem": "^2.21.3"
18
- },
19
- "devDependencies": {
20
- "@types/node": "^20.9.0",
21
- "eslint": "^8.53.0",
22
- "eslint-config-ponder": "^0.0.95",
23
- "typescript": "^5.2.2"
24
- },
25
- "engines": {
26
- "node": ">=18.14"
27
- }
28
- }
@@ -1,15 +0,0 @@
1
- /// <reference types="ponder/virtual" />
2
-
3
- declare module "ponder:internal" {
4
- const config: typeof import("./ponder.config.ts");
5
- const schema: typeof import("./ponder.schema.ts");
6
- }
7
-
8
- declare module "ponder:schema" {
9
- export * from "./ponder.schema.ts";
10
- }
11
-
12
- // This file enables type checking and editor autocomplete for this Ponder project.
13
- // After upgrading, you may find that changes have been made to this file.
14
- // If this happens, please commit the changes. Do not manually edit this file.
15
- // See https://ponder.sh/docs/requirements#typescript for more information.
@@ -1,6 +0,0 @@
1
- import { onchainTable } from "ponder";
2
-
3
- export const example = onchainTable("example", (t) => ({
4
- id: t.text().primaryKey(),
5
- name: t.text(),
6
- }));
@@ -1,26 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Type checking
4
- "strict": true,
5
- "noUncheckedIndexedAccess": true,
6
-
7
- // Interop constraints
8
- "verbatimModuleSyntax": false,
9
- "esModuleInterop": true,
10
- "isolatedModules": true,
11
- "allowSyntheticDefaultImports": true,
12
- "resolveJsonModule": true,
13
-
14
- // Language and environment
15
- "moduleResolution": "bundler",
16
- "module": "ESNext",
17
- "noEmit": true,
18
- "lib": ["ES2022"],
19
- "target": "ES2022",
20
-
21
- // Skip type checking for node modules
22
- "skipLibCheck": true
23
- },
24
- "include": ["./**/*.ts"],
25
- "exclude": ["node_modules"]
26
- }