@clarigen/cli 0.2.0 → 0.3.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/README.md CHANGED
@@ -6,66 +6,8 @@ Most of the time, you'll only need to run `clarigen` to automatically build your
6
6
 
7
7
  # Usage
8
8
 
9
- <!-- usage -->
10
- ```sh-session
11
- $ npm install -g @clarigen/cli
12
- $ clarigen COMMAND
13
- running command...
14
- $ clarigen (-v|--version|version)
15
- @clarigen/cli/0.1.1 darwin-arm64 node-v16.4.2
16
- $ clarigen --help [COMMAND]
17
- USAGE
18
- $ clarigen COMMAND
19
- ...
20
- ```
21
- <!-- usagestop -->
22
-
23
9
  <!-- commands -->
24
- * [`clarigen `](#clarigen-)
25
- * [`clarigen contract [CONTRACT]`](#clarigen-contract-contract)
26
- * [`clarigen generate`](#clarigen-generate)
27
-
28
- ## `clarigen `
29
-
30
- Generate project files
31
-
32
- ```
33
- USAGE
34
- $ clarigen
35
-
36
- OPTIONS
37
- -h, --help show CLI help
38
- -w, --watch Watch for changes to your contracts
39
- ```
40
-
41
- ## `clarigen contract [CONTRACT]`
42
-
43
- Generate files for a single contract
44
10
 
45
- ```
46
- USAGE
47
- $ clarigen contract [CONTRACT]
48
-
49
- ARGUMENTS
50
- CONTRACT The file path for your contract
51
-
52
- OPTIONS
53
- -h, --help show CLI help
54
- -o, --output=output [default: clarion] Output destination folder
55
- ```
56
-
57
- ## `clarigen generate`
58
-
59
- Generate project files
60
-
61
- ```
62
- USAGE
63
- $ clarigen generate
64
-
65
- OPTIONS
66
- -h, --help show CLI help
67
- -w, --watch Watch for changes to your contracts
68
- ```
69
11
  <!-- commandsstop -->
70
12
 
71
13
  ## Development
package/bin/run CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ /* eslint-disable @typescript-eslint/no-var-requires */
2
3
 
3
- require('../dist')
4
+ require('@oclif/command')
4
5
  .run()
5
6
  .then(require('@oclif/command/flush'))
6
7
  .catch(require('@oclif/errors/handle'));
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getClarinetAccounts = exports.getContractsFromClarinet = exports.getClarinetConfig = exports.getClarinetDevConfig = void 0;
4
+ const j_toml_1 = require("@ltd/j-toml");
5
+ const path_1 = require("path");
6
+ const promises_1 = require("fs/promises");
7
+ const wallet_sdk_1 = require("@stacks/wallet-sdk");
8
+ async function getClarinetDevConfig(folder) {
9
+ const baseConfigPath = path_1.resolve(folder, 'settings', 'Devnet.toml');
10
+ const configContents = await promises_1.readFile(baseConfigPath, { encoding: 'utf-8' });
11
+ const config = j_toml_1.parse(configContents, 1.0, '\n', true, {
12
+ longer: true,
13
+ });
14
+ return config;
15
+ }
16
+ exports.getClarinetDevConfig = getClarinetDevConfig;
17
+ async function getClarinetConfig(folder) {
18
+ const baseConfigPath = path_1.resolve(folder, 'Clarinet.toml');
19
+ const configContents = await promises_1.readFile(baseConfigPath, { encoding: 'utf-8' });
20
+ const config = j_toml_1.parse(configContents, 1.0, '\n', true);
21
+ return config;
22
+ }
23
+ exports.getClarinetConfig = getClarinetConfig;
24
+ async function getContractsFromClarinet(folder, accounts) {
25
+ const clarinetConfig = await getClarinetConfig(folder);
26
+ const deployerAddress = accounts.deployer.address;
27
+ const contracts = Object.entries(clarinetConfig.contracts).map(([contractName, info]) => {
28
+ const file = info.path.replace(/^contracts\//, '');
29
+ return {
30
+ file,
31
+ address: deployerAddress,
32
+ };
33
+ });
34
+ return contracts;
35
+ }
36
+ exports.getContractsFromClarinet = getContractsFromClarinet;
37
+ async function getClarinetAccounts(folder) {
38
+ const devConfig = await getClarinetDevConfig(folder);
39
+ const accountEntries = await Promise.all(Object.entries(devConfig.accounts).map(async ([key, info]) => {
40
+ const wallet = await wallet_sdk_1.generateWallet({
41
+ secretKey: info.mnemonic,
42
+ password: 'password',
43
+ });
44
+ const [account] = wallet.accounts;
45
+ const address = wallet_sdk_1.getStxAddress({ account });
46
+ return [
47
+ key,
48
+ {
49
+ ...info,
50
+ address,
51
+ },
52
+ ];
53
+ }));
54
+ const accounts = Object.fromEntries(accountEntries);
55
+ return accounts;
56
+ }
57
+ exports.getClarinetAccounts = getClarinetAccounts;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Contract = void 0;
4
+ const command_1 = require("@oclif/command");
5
+ const utils_1 = require("../utils");
6
+ class Contract extends command_1.Command {
7
+ async run() {
8
+ const { argv, flags } = this.parse(Contract);
9
+ const [contractFile] = argv;
10
+ await utils_1.generateFilesForContract({
11
+ contractFile,
12
+ outputFolder: flags.output,
13
+ });
14
+ }
15
+ }
16
+ exports.Contract = Contract;
17
+ Contract.description = `Generate files for a single contract`;
18
+ Contract.strict = true;
19
+ Contract.hidden = true;
20
+ Contract.flags = {
21
+ help: command_1.flags.help({ char: 'h' }),
22
+ output: command_1.flags.string({
23
+ char: 'o',
24
+ description: 'Output destination folder',
25
+ default: 'clarion',
26
+ }),
27
+ };
28
+ Contract.args = [
29
+ {
30
+ name: 'contract',
31
+ description: 'The file path for your contract',
32
+ },
33
+ ];
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Generate = void 0;
4
+ const command_1 = require("@oclif/command");
5
+ const utils_1 = require("../utils");
6
+ const config_1 = require("../config");
7
+ const chokidar_1 = require("chokidar");
8
+ const path_1 = require("path");
9
+ const chalk_1 = require("chalk");
10
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
11
+ const ora = require('ora');
12
+ class Generate extends command_1.Command {
13
+ async run() {
14
+ const { flags } = this.parse(Generate);
15
+ const cwd = process.cwd();
16
+ if (flags.watch) {
17
+ const spinner = ora('Generating files').start();
18
+ const { contractsDir } = await config_1.getConfigFile(cwd);
19
+ const watcher = chokidar_1.watch([contractsDir], {
20
+ cwd,
21
+ });
22
+ try {
23
+ await utils_1.generateProject(cwd);
24
+ spinner.succeed(`Finished generating files. Watching for changes.`);
25
+ }
26
+ catch (error) {
27
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
28
+ spinner.fail(`Error generating files.\n${error.message}`);
29
+ }
30
+ watcher.on('change', async (path) => {
31
+ const file = path_1.basename(path);
32
+ spinner.clear();
33
+ spinner.start(`Change detected for ${chalk_1.green(file)}, generating.`);
34
+ try {
35
+ await utils_1.generateProject(cwd);
36
+ spinner.succeed(`Finished generating files for ${chalk_1.green(file)}. Watching for changes.`);
37
+ }
38
+ catch (error) {
39
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
40
+ const msg = error.message;
41
+ spinner.fail(`Error after saving ${chalk_1.red(file)}.\n${msg}`);
42
+ }
43
+ });
44
+ process.on('SIGINT', async () => {
45
+ await watcher.close();
46
+ process.exit();
47
+ });
48
+ }
49
+ else {
50
+ await utils_1.generateProject(cwd);
51
+ }
52
+ }
53
+ }
54
+ exports.Generate = Generate;
55
+ Generate.description = `Generate project files`;
56
+ Generate.strict = true;
57
+ Generate.hidden = false;
58
+ Generate.flags = {
59
+ help: command_1.flags.help({ char: 'h' }),
60
+ watch: command_1.flags.boolean({
61
+ char: 'w',
62
+ description: 'Watch for changes to your contracts',
63
+ }),
64
+ };
65
+ Generate.args = [];
package/dist/config.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getConfigFile = void 0;
4
+ const path_1 = require("path");
5
+ const promises_1 = require("fs/promises");
6
+ const clarinet_config_1 = require("./clarinet-config");
7
+ async function getConfigFile(rootPath) {
8
+ const fullPath = path_1.resolve(rootPath, 'clarigen.config.json');
9
+ const configContents = await promises_1.readFile(fullPath, { encoding: 'utf-8' });
10
+ const configFile = JSON.parse(configContents);
11
+ if (!configFile.outputDir)
12
+ throw new Error('Config file missing "outputDir"');
13
+ if ('clarinet' in configFile) {
14
+ const clarinetPath = path_1.resolve(rootPath, configFile.clarinet);
15
+ const accounts = await clarinet_config_1.getClarinetAccounts(clarinetPath);
16
+ const contracts = await clarinet_config_1.getContractsFromClarinet(clarinetPath, accounts);
17
+ const contractsDir = path_1.relative(process.cwd(), path_1.join(configFile.clarinet, 'contracts'));
18
+ return {
19
+ ...configFile,
20
+ contracts,
21
+ contractsDir,
22
+ accounts,
23
+ };
24
+ }
25
+ if (!configFile.contracts)
26
+ throw new Error('Config file missing "contracts"');
27
+ if (!configFile.contractsDir)
28
+ throw new Error('Config file missing "contractDir"');
29
+ return configFile;
30
+ }
31
+ exports.getConfigFile = getConfigFile;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeTypes = exports.jsTypeFromAbiType = exports.cvFromType = void 0;
4
+ const transactions_1 = require("@stacks/transactions");
5
+ const core_1 = require("@clarigen/core");
6
+ const cvFromType = (val) => {
7
+ if (transactions_1.isClarityAbiPrimitive(val)) {
8
+ if (val === 'uint128') {
9
+ return 'ClarityTypes.UIntCV';
10
+ }
11
+ else if (val === 'int128') {
12
+ return 'ClarityTypes.IntCV';
13
+ }
14
+ else if (val === 'bool') {
15
+ return 'ClarityTypes.BooleanCV';
16
+ }
17
+ else if (val === 'principal') {
18
+ return 'ClarityTypes.PrincipalCV';
19
+ }
20
+ else if (val === 'none') {
21
+ return 'ClarityTypes.NoneCV';
22
+ }
23
+ else {
24
+ throw new Error(`Unexpected Clarity ABI type primitive: ${JSON.stringify(val)}`);
25
+ }
26
+ }
27
+ else if (transactions_1.isClarityAbiBuffer(val)) {
28
+ return 'ClarityTypes.BufferCV';
29
+ }
30
+ else if (transactions_1.isClarityAbiResponse(val)) {
31
+ return 'ClarityTypes.ResponseCV';
32
+ }
33
+ else if (transactions_1.isClarityAbiOptional(val)) {
34
+ return 'ClarityTypes.OptionalCV';
35
+ }
36
+ else if (transactions_1.isClarityAbiTuple(val)) {
37
+ return 'ClarityTypes.TupleCV';
38
+ }
39
+ else if (transactions_1.isClarityAbiList(val)) {
40
+ return 'ClarityTypes.ListCV';
41
+ }
42
+ else if (transactions_1.isClarityAbiStringAscii(val)) {
43
+ return 'ClarityTypes.StringAsciiCV';
44
+ }
45
+ else if (transactions_1.isClarityAbiStringUtf8(val)) {
46
+ return 'ClarityTypes.StringUtf8CV';
47
+ }
48
+ else {
49
+ throw new Error(`Unexpected Clarity ABI type: ${JSON.stringify(val)}`);
50
+ }
51
+ };
52
+ exports.cvFromType = cvFromType;
53
+ const jsTypeFromAbiType = (val, isArgument = false) => {
54
+ if (transactions_1.isClarityAbiPrimitive(val)) {
55
+ if (val === 'uint128') {
56
+ if (isArgument)
57
+ return 'number | bigint';
58
+ return 'bigint';
59
+ }
60
+ else if (val === 'int128') {
61
+ if (isArgument)
62
+ return 'number | bigint';
63
+ return 'bigint';
64
+ }
65
+ else if (val === 'bool') {
66
+ return 'boolean';
67
+ }
68
+ else if (val === 'principal') {
69
+ return 'string';
70
+ }
71
+ else if (val === 'none') {
72
+ return 'null';
73
+ }
74
+ else if (val === 'trait_reference') {
75
+ return 'string';
76
+ }
77
+ else {
78
+ throw new Error(`Unexpected Clarity ABI type primitive: ${JSON.stringify(val)}`);
79
+ }
80
+ }
81
+ else if (transactions_1.isClarityAbiBuffer(val)) {
82
+ return 'Buffer';
83
+ }
84
+ else if (transactions_1.isClarityAbiResponse(val)) {
85
+ const ok = exports.jsTypeFromAbiType(val.response.ok);
86
+ const err = exports.jsTypeFromAbiType(val.response.error);
87
+ return `ClarityTypes.Response<${ok}, ${err}>`;
88
+ }
89
+ else if (transactions_1.isClarityAbiOptional(val)) {
90
+ const innerType = exports.jsTypeFromAbiType(val.optional);
91
+ return `${innerType} | null`;
92
+ }
93
+ else if (transactions_1.isClarityAbiTuple(val)) {
94
+ const tupleDefs = [];
95
+ val.tuple.forEach(({ name, type }) => {
96
+ const innerType = exports.jsTypeFromAbiType(type);
97
+ tupleDefs.push(`"${name}": ${innerType}`);
98
+ });
99
+ return `{
100
+ ${tupleDefs.join(';\n ')}
101
+ }`;
102
+ }
103
+ else if (transactions_1.isClarityAbiList(val)) {
104
+ const innerType = exports.jsTypeFromAbiType(val.list.type);
105
+ return `${innerType}[]`;
106
+ }
107
+ else if (transactions_1.isClarityAbiStringAscii(val)) {
108
+ return 'string';
109
+ }
110
+ else if (transactions_1.isClarityAbiStringUtf8(val)) {
111
+ return 'string';
112
+ }
113
+ else if (val === 'trait_reference') {
114
+ return 'string';
115
+ }
116
+ else {
117
+ throw new Error(`Unexpected Clarity ABI type: ${JSON.stringify(val)}`);
118
+ }
119
+ };
120
+ exports.jsTypeFromAbiType = jsTypeFromAbiType;
121
+ const makeTypes = (abi) => {
122
+ let typings = '';
123
+ abi.functions.forEach((func, index) => {
124
+ if (func.access === 'private')
125
+ return;
126
+ let functionLine = `${core_1.toCamelCase(func.name)}: `;
127
+ const args = func.args.map((arg) => {
128
+ return `${core_1.toCamelCase(arg.name)}: ${exports.jsTypeFromAbiType(arg.type, true)}`;
129
+ });
130
+ functionLine += `(${args.join(', ')}) => `;
131
+ if (func.access === 'public') {
132
+ const { type } = func.outputs;
133
+ if (!transactions_1.isClarityAbiResponse(type))
134
+ throw new Error('Expected response type for public function');
135
+ functionLine += 'Transaction';
136
+ const ok = exports.jsTypeFromAbiType(type.response.ok);
137
+ const err = exports.jsTypeFromAbiType(type.response.error);
138
+ functionLine += `<${ok}, ${err}>;`;
139
+ }
140
+ else {
141
+ const jsType = exports.jsTypeFromAbiType(func.outputs.type);
142
+ functionLine += `Promise<${jsType}>;`;
143
+ // const { type } = func.outputs;
144
+ // if (isClarityAbiResponse(type)) {
145
+ // const ok = jsTypeFromAbiType(type.response.ok);
146
+ // const err = jsTypeFromAbiType(type.response.error);
147
+ // functionLine += `Promise<ClarityTypes.Response<${ok}, ${err}>>;`;
148
+ // } else {
149
+ // const jsType = jsTypeFromAbiType(func.outputs.type);
150
+ // functionLine += `Promise<${jsType}>;`;
151
+ // }
152
+ }
153
+ typings += `${index === 0 ? '' : '\n'} ${functionLine}`;
154
+ });
155
+ return typings;
156
+ };
157
+ exports.makeTypes = makeTypes;
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateProjectIndexFile = exports.generateTypesFile = exports.generateIndexFile = exports.generateInterfaceFile = exports.generateInterface = void 0;
4
+ const native_bin_1 = require("@clarigen/native-bin");
5
+ const core_1 = require("@clarigen/core");
6
+ const declaration_1 = require("./declaration");
7
+ const path_1 = require("path");
8
+ const generateInterface = async ({ provider: _provider, contractFile, contractAddress = 'S1G2081040G2081040G2081040G208105NK8PE5', }) => {
9
+ const provider = _provider || (await native_bin_1.createClarityBin());
10
+ const contractName = core_1.getContractNameFromPath(contractFile);
11
+ const receipt = await provider.runCommand([
12
+ 'launch',
13
+ `${contractAddress}.${contractName}`,
14
+ contractFile,
15
+ provider.dbFilePath,
16
+ '--output_analysis',
17
+ '--costs',
18
+ '--assets',
19
+ ]);
20
+ if (receipt.stderr &&
21
+ !receipt.stderr.includes('Used unimplemented cost function')) {
22
+ throw new Error(`Error on ${contractFile}:
23
+ ${receipt.stderr}
24
+ `);
25
+ }
26
+ const output = JSON.parse(receipt.stdout);
27
+ if (output.error) {
28
+ const { initialization } = output.error;
29
+ if (initialization?.includes('\nNear:\n')) {
30
+ const [error, trace] = initialization.split('\nNear:\n');
31
+ let startLine = '';
32
+ const matcher = /start_line: (\d+),/;
33
+ const matches = matcher.exec(trace);
34
+ if (matches)
35
+ startLine = matches[1];
36
+ throw new Error(`Error on ${contractFile}:
37
+ ${error}
38
+ ${startLine ? `Near line ${startLine}` : ''}
39
+ Raw trace:
40
+ ${trace}
41
+ `);
42
+ }
43
+ throw new Error(`Error on ${contractFile}:
44
+ ${JSON.stringify(output.error, null, 2)}
45
+ `);
46
+ }
47
+ const abi = output.analysis;
48
+ return abi;
49
+ };
50
+ exports.generateInterface = generateInterface;
51
+ const generateInterfaceFile = ({ contractFile, abi, }) => {
52
+ const contractName = core_1.getContractNameFromPath(contractFile);
53
+ const variableName = core_1.toCamelCase(contractName, true);
54
+ const abiString = JSON.stringify(abi, null, 2);
55
+ const fileContents = `import { ClarityAbi } from '@clarigen/core';
56
+
57
+ // prettier-ignore
58
+ export const ${variableName}Interface: ClarityAbi = ${abiString};
59
+ `;
60
+ return fileContents;
61
+ };
62
+ exports.generateInterfaceFile = generateInterfaceFile;
63
+ const generateIndexFile = ({ contractFile, address, }) => {
64
+ const contractName = core_1.getContractNameFromPath(contractFile);
65
+ const contractTitle = core_1.toCamelCase(contractName, true);
66
+ const varName = core_1.toCamelCase(contractName);
67
+ const contractType = `${contractTitle}Contract`;
68
+ const fileContents = `import { proxy, BaseProvider, Contract } from '@clarigen/core';
69
+ import type { ${contractType} } from './types';
70
+ import { ${contractTitle}Interface } from './abi';
71
+ export type { ${contractType} } from './types';
72
+
73
+ export const ${varName}Contract = (provider: BaseProvider) => {
74
+ const contract = proxy<${contractType}>(${contractTitle}Interface, provider);
75
+ return contract;
76
+ };
77
+
78
+ export const ${varName}Info: Contract<${contractType}> = {
79
+ contract: ${varName}Contract,
80
+ address: '${address}',
81
+ contractFile: '${contractFile}',
82
+ };
83
+ `;
84
+ return fileContents;
85
+ };
86
+ exports.generateIndexFile = generateIndexFile;
87
+ const generateTypesFile = (abi, contractName) => {
88
+ const name = core_1.toCamelCase(contractName, true);
89
+ const typings = declaration_1.makeTypes(abi);
90
+ const fileContents = `import { ClarityTypes, Transaction } from '@clarigen/core';
91
+
92
+ // prettier-ignore
93
+ export interface ${name}Contract {
94
+ ${typings}
95
+ }
96
+ `;
97
+ return fileContents;
98
+ };
99
+ exports.generateTypesFile = generateTypesFile;
100
+ const generateProjectIndexFile = (config) => {
101
+ const imports = [];
102
+ const exports = [];
103
+ const contractMap = [];
104
+ let accounts = '';
105
+ if ('accounts' in config) {
106
+ const accountLines = Object.keys(config.accounts).map((key) => {
107
+ const account = config.accounts[key];
108
+ return `"${key}": {
109
+ mnemonic: "${account.mnemonic}",
110
+ balance: ${account.balance.toString()}n,
111
+ address: "${account.address}",
112
+ },`;
113
+ });
114
+ accounts = `\n\n// prettier-ignore
115
+ export const accounts = {
116
+ ${accountLines.join('\n ')}
117
+ };`;
118
+ }
119
+ config.contracts.forEach((contract) => {
120
+ const contractName = core_1.getContractNameFromPath(contract.file);
121
+ const contractVar = core_1.toCamelCase(contractName);
122
+ const contractInfo = `${contractVar}Info`;
123
+ const contractInterface = `${core_1.toCamelCase(contractName, true)}Contract`;
124
+ const dirName = path_1.dirname(contract.file);
125
+ const importPath = `'./${path_1.join(dirName || '.', contractName)}'`;
126
+ const _import = `import { ${contractInfo} } from ${importPath};`;
127
+ imports.push(_import);
128
+ const _export = `export type { ${contractInterface} } from ${importPath};`;
129
+ exports.push(_export);
130
+ const map = `${contractVar}: ${contractInfo},`;
131
+ contractMap.push(map);
132
+ });
133
+ const file = `${imports.join('\n')}
134
+ ${exports.join('\n')}
135
+
136
+ export const contracts = {
137
+ ${contractMap.join('\n ')}
138
+ };${accounts}
139
+ `;
140
+ return file;
141
+ };
142
+ exports.generateProjectIndexFile = generateProjectIndexFile;
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.run = void 0;
14
+ var command_1 = require("@oclif/command");
15
+ Object.defineProperty(exports, "run", { enumerable: true, get: function () { return command_1.run; } });
16
+ __exportStar(require("./config"), exports);
17
+ __exportStar(require("./generate/declaration"), exports);
18
+ __exportStar(require("./generate/files"), exports);
19
+ __exportStar(require("./commands"), exports);
package/dist/start.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const errors_1 = require("@oclif/errors");
7
+ const flush_1 = __importDefault(require("@oclif/command/flush"));
8
+ const index_1 = require("./index");
9
+ index_1.run().then(flush_1.default, errors_1.handle);
package/dist/utils.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateProject = exports.generateFilesForContract = void 0;
4
+ const files_1 = require("./generate/files");
5
+ const core_1 = require("@clarigen/core");
6
+ const native_bin_1 = require("@clarigen/native-bin");
7
+ const path_1 = require("path");
8
+ const promises_1 = require("fs/promises");
9
+ const config_1 = require("./config");
10
+ const generateFilesForContract = async ({ contractFile: _contractFile, outputFolder, provider, contractAddress, dirName, }) => {
11
+ const contractFile = path_1.resolve(process.cwd(), _contractFile);
12
+ const contractName = core_1.getContractNameFromPath(contractFile);
13
+ const abi = await files_1.generateInterface({
14
+ contractFile,
15
+ provider,
16
+ contractAddress,
17
+ });
18
+ const typesFile = files_1.generateTypesFile(abi, contractName);
19
+ if (!contractAddress && process.env.NODE_ENV !== 'test') {
20
+ console.warn('Please provide an address with every contract.');
21
+ }
22
+ const indexFile = files_1.generateIndexFile({
23
+ contractFile: path_1.relative(process.cwd(), contractFile),
24
+ address: contractAddress || '',
25
+ });
26
+ const abiFile = files_1.generateInterfaceFile({ contractFile, abi });
27
+ const baseDir = outputFolder || path_1.resolve(process.cwd(), `tmp/${contractName}`);
28
+ const outputPath = path_1.resolve(baseDir, dirName || '.', contractName);
29
+ await promises_1.mkdir(outputPath, { recursive: true });
30
+ await promises_1.writeFile(path_1.resolve(outputPath, 'abi.ts'), abiFile);
31
+ await promises_1.writeFile(path_1.resolve(outputPath, 'index.ts'), indexFile);
32
+ await promises_1.writeFile(path_1.resolve(outputPath, 'types.ts'), typesFile);
33
+ };
34
+ exports.generateFilesForContract = generateFilesForContract;
35
+ const generateProject = async (projectPath) => {
36
+ const configFile = await config_1.getConfigFile(projectPath);
37
+ const { contractsDir, outputDir, contracts } = configFile;
38
+ const outputFolder = path_1.resolve(projectPath, outputDir);
39
+ const provider = await native_bin_1.createClarityBin();
40
+ // this needs to be serial
41
+ for (const contract of contracts) {
42
+ const contractFile = path_1.resolve(projectPath, contractsDir, contract.file);
43
+ const dirName = path_1.dirname(contract.file);
44
+ await exports.generateFilesForContract({
45
+ contractFile,
46
+ outputFolder: outputFolder,
47
+ provider,
48
+ contractAddress: contract.address,
49
+ dirName,
50
+ });
51
+ }
52
+ const indexFile = files_1.generateProjectIndexFile(configFile);
53
+ const indexPath = path_1.resolve(outputFolder, 'index.ts');
54
+ await promises_1.writeFile(indexPath, indexFile);
55
+ };
56
+ exports.generateProject = generateProject;
package/package.json CHANGED
@@ -2,13 +2,13 @@
2
2
  "name": "@clarigen/cli",
3
3
  "description": "A CLI for generating a Typescript interface for a Clarity contract.",
4
4
  "author": "Hank Stoever",
5
- "version": "0.2.0",
5
+ "version": "0.3.0",
6
6
  "license": "MIT",
7
7
  "main": "dist/index.js",
8
8
  "files": [
9
9
  "dist",
10
10
  "src",
11
- "/oclif.manifest.json"
11
+ "oclif.manifest.json"
12
12
  ],
13
13
  "engines": {
14
14
  "node": ">=10"
@@ -18,10 +18,11 @@
18
18
  "start": "ts-node-script src/start.ts",
19
19
  "compile": "ncc build src/index.ts --no-source-map-register -e @oclif/command -e @oclif/errors",
20
20
  "build-single-file": "yarn compile && oclif-dev readme",
21
- "build": "tsdx build && oclif-dev readme",
21
+ "build": "rm -rf dist && tsc -b",
22
22
  "test": "tsdx test",
23
23
  "lint": "tsdx lint",
24
- "typecheck": "tsc --noEmit"
24
+ "typecheck": "tsc --noEmit",
25
+ "prepublishOnly": "yarn build"
25
26
  },
26
27
  "bin": {
27
28
  "clarigen": "./bin/run"
@@ -37,25 +38,26 @@
37
38
  "trailingComma": "es5"
38
39
  },
39
40
  "devDependencies": {
40
- "@oclif/config": "^1.17.0",
41
41
  "@oclif/dev-cli": "^1.26.0",
42
42
  "@oclif/errors": "^1.3.4",
43
43
  "@vercel/ncc": "0.27.0",
44
- "chokidar": "3.5.1",
45
44
  "oclif": "^1.16.1",
46
45
  "ts-node": "^9.1.1"
47
46
  },
48
47
  "dependencies": {
49
- "@clarigen/core": "^0.2.0",
50
- "@clarigen/native-bin": "^0.2.0",
48
+ "@clarigen/core": "0.2.3",
49
+ "@clarigen/native-bin": "0.2.7",
51
50
  "@ltd/j-toml": "1.12.2",
52
51
  "@oclif/command": "^1.8.0",
52
+ "@oclif/config": "^1.17.0",
53
+ "@oclif/plugin-help": "3.2.3",
53
54
  "@stacks/wallet-sdk": "1.0.0-wallet-sdk.4",
54
55
  "chalk": "4.1.0",
56
+ "chokidar": "3.5.1",
55
57
  "ora": "5.4.0"
56
58
  },
57
59
  "publishConfig": {
58
60
  "access": "public"
59
61
  },
60
- "gitHead": "c8f86f1c4793471c0df2ae80e25aaf7562d52eeb"
62
+ "gitHead": "1a82667ab26f312b401cbbd84aca4d1879e7e1a6"
61
63
  }
@@ -24,12 +24,9 @@ export async function getClarinetDevConfig(
24
24
  ): Promise<ClarinetDevConfig> {
25
25
  const baseConfigPath = resolve(folder, 'settings', 'Devnet.toml');
26
26
  const configContents = await readFile(baseConfigPath, { encoding: 'utf-8' });
27
- const config = (parse(
28
- configContents,
29
- 1.0,
30
- '\n',
31
- false
32
- ) as unknown) as ClarinetDevConfig;
27
+ const config = (parse(configContents, 1.0, '\n', true, {
28
+ longer: true,
29
+ }) as unknown) as ClarinetDevConfig;
33
30
  return config;
34
31
  }
35
32
 
@@ -49,7 +46,7 @@ export async function getClarinetConfig(folder: string) {
49
46
  configContents,
50
47
  1.0,
51
48
  '\n',
52
- false
49
+ true
53
50
  ) as unknown) as ClarinetConfig;
54
51
  return config;
55
52
  }
@@ -4,6 +4,7 @@ import { generateFilesForContract } from '../utils';
4
4
  export class Contract extends Command {
5
5
  static description = `Generate files for a single contract`;
6
6
  static strict = true;
7
+ static hidden = true;
7
8
 
8
9
  static flags = {
9
10
  help: flags.help({ char: 'h' }),
@@ -1,3 +1,67 @@
1
- import { Generate } from './generate';
1
+ import { Command, flags } from '@oclif/command';
2
+ import { generateProject } from '../utils';
3
+ import { getConfigFile } from '../config';
4
+ import { watch } from 'chokidar';
5
+ import { basename } from 'path';
6
+ import { red, green } from 'chalk';
7
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
8
+ const ora = require('ora');
2
9
 
3
- export default Generate;
10
+ export class Generate extends Command {
11
+ static description = `Generate project files`;
12
+ static strict = true;
13
+ static hidden = false;
14
+
15
+ static flags = {
16
+ help: flags.help({ char: 'h' }),
17
+ watch: flags.boolean({
18
+ char: 'w',
19
+ description: 'Watch for changes to your contracts',
20
+ }),
21
+ };
22
+
23
+ static args = [];
24
+
25
+ async run() {
26
+ const { flags } = this.parse(Generate);
27
+ const cwd = process.cwd();
28
+
29
+ if (flags.watch) {
30
+ const spinner = ora('Generating files').start();
31
+ const { contractsDir } = await getConfigFile(cwd);
32
+ const watcher = watch([contractsDir], {
33
+ cwd,
34
+ });
35
+ try {
36
+ await generateProject(cwd);
37
+ spinner.succeed(`Finished generating files. Watching for changes.`);
38
+ } catch (error) {
39
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
40
+ spinner.fail(`Error generating files.\n${(error as any).message}`);
41
+ }
42
+ watcher.on('change', async (path) => {
43
+ const file = basename(path);
44
+ spinner.clear();
45
+ spinner.start(`Change detected for ${green(file)}, generating.`);
46
+ try {
47
+ await generateProject(cwd);
48
+ spinner.succeed(
49
+ `Finished generating files for ${green(
50
+ file
51
+ )}. Watching for changes.`
52
+ );
53
+ } catch (error) {
54
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
55
+ const msg = (error as any).message;
56
+ spinner.fail(`Error after saving ${red(file)}.\n${msg}`);
57
+ }
58
+ });
59
+ process.on('SIGINT', async () => {
60
+ await watcher.close();
61
+ process.exit();
62
+ });
63
+ } else {
64
+ await generateProject(cwd);
65
+ }
66
+ }
67
+ }
@@ -8,7 +8,7 @@ import {
8
8
  getContractNameFromPath,
9
9
  } from '@clarigen/core';
10
10
  import { makeTypes } from './declaration';
11
- import { ConfigContract, ConfigFile } from 'src/config';
11
+ import { ConfigContract, ConfigFile } from '../config';
12
12
  import { dirname, resolve, join } from 'path';
13
13
 
14
14
  export const generateInterface = async ({
@@ -31,7 +31,10 @@ export const generateInterface = async ({
31
31
  '--costs',
32
32
  '--assets',
33
33
  ]);
34
- if (receipt.stderr) {
34
+ if (
35
+ receipt.stderr &&
36
+ !receipt.stderr.includes('Used unimplemented cost function')
37
+ ) {
35
38
  throw new Error(`Error on ${contractFile}:
36
39
  ${receipt.stderr}
37
40
  `);
@@ -133,8 +136,19 @@ export const generateProjectIndexFile = (config: ConfigFile) => {
133
136
 
134
137
  let accounts = '';
135
138
  if ('accounts' in config) {
139
+ const accountLines = Object.keys(config.accounts).map((key) => {
140
+ const account = config.accounts[key];
141
+ return `"${key}": {
142
+ mnemonic: "${account.mnemonic}",
143
+ balance: ${account.balance.toString()}n,
144
+ address: "${account.address}",
145
+ },`;
146
+ });
147
+
136
148
  accounts = `\n\n// prettier-ignore
137
- export const accounts = ${JSON.stringify(config.accounts, null, 2)};`;
149
+ export const accounts = {
150
+ ${accountLines.join('\n ')}
151
+ };`;
138
152
  }
139
153
 
140
154
  config.contracts.forEach((contract) => {
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@ export { run } from '@oclif/command';
2
2
  export * from './config';
3
3
  export * from './generate/declaration';
4
4
  export * from './generate/files';
5
+ export * from './commands';
package/CHANGELOG.md DELETED
@@ -1,29 +0,0 @@
1
- # @clarigen/cli
2
-
3
- ## 0.2.0
4
- ### Minor Changes
5
-
6
- - fd1b36b: This version contains a number of big changes!
7
-
8
- My favorite addition is the addition of a `@clarigen/native-bin` package. This removes the need to manually build and specify your local version of the `clarity-cli` binary. Most of this code was taken from the `@blockstack/clarity-native-bin` library. Users on Apple Silicon will also automatically download a pre-built binary (which was not previously available).
9
-
10
- These changes should really improve DX for project setup, as well as the ability to run Clarigen in CI.
11
-
12
- Other changes:
13
-
14
- - **Breaking change** JS types for functions that return `uint` or `int` types now use the `BigInt` native JavaScript type.
15
- - Although this is a breaking change, this is not a major update. Clarigen is still 0.x and is not in wide use.
16
- - **Breaking change**: Projects that have a `clarinet` configuration now look for the `Devnet.toml` file (instead of `Development.toml`)
17
- - Clarity type conversion (between hex and JS) has been consolidated into the `core` library
18
- - Bug fixes for boolean type conversion for function parameters
19
- - Added `get-stx-balance` test utility function
20
- - Added util for getting boot contract identifiers
21
- - `clarity-cli` is now launched in testnet mode by default
22
- - Added changesets for better release management
23
- - Added Github Actions CI to this package
24
-
25
- ### Patch Changes
26
-
27
- - Updated dependencies [fd1b36b]
28
- - @clarigen/core@0.2.0
29
- - @clarigen/native-bin@0.2.0
@@ -1,63 +0,0 @@
1
- import { Command, flags } from '@oclif/command';
2
- import { generateProject } from '../utils';
3
- import { getConfigFile } from '../config';
4
- import { watch } from 'chokidar';
5
- import { basename } from 'path';
6
- import { red, green } from 'chalk';
7
- // eslint-disable-next-line @typescript-eslint/no-var-requires
8
- const ora = require('ora');
9
-
10
- export class Generate extends Command {
11
- static description = `Generate project files`;
12
- static strict = true;
13
-
14
- static flags = {
15
- help: flags.help({ char: 'h' }),
16
- watch: flags.boolean({
17
- char: 'w',
18
- description: 'Watch for changes to your contracts',
19
- }),
20
- };
21
-
22
- static args = [];
23
-
24
- async run() {
25
- const { flags } = this.parse(Generate);
26
- const cwd = process.cwd();
27
-
28
- if (flags.watch) {
29
- const spinner = ora('Generating files').start();
30
- const { contractsDir } = await getConfigFile(cwd);
31
- const watcher = watch([contractsDir], {
32
- cwd,
33
- });
34
- try {
35
- await generateProject(cwd);
36
- spinner.succeed(`Finished generating files. Watching for changes.`);
37
- } catch (error: any) {
38
- spinner.fail(`Error generating files.\n${error.message}`);
39
- }
40
- watcher.on('change', async (path) => {
41
- const file = basename(path);
42
- spinner.clear();
43
- spinner.start(`Change detected for ${green(file)}, generating.`);
44
- try {
45
- await generateProject(cwd);
46
- spinner.succeed(
47
- `Finished generating files for ${green(
48
- file
49
- )}. Watching for changes.`
50
- );
51
- } catch (error: any) {
52
- spinner.fail(`Error after saving ${red(file)}.\n${error.message}`);
53
- }
54
- });
55
- process.on('SIGINT', async () => {
56
- await watcher.close();
57
- process.exit();
58
- });
59
- } else {
60
- await generateProject(cwd);
61
- }
62
- }
63
- }