Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS Templates #116

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b133e81
Add node_modules
MaxStalker Feb 9, 2021
a9b4760
Scaffold generator templates
MaxStalker Feb 9, 2021
5c5750c
Init package. Add prettier, jest and babel configs
MaxStalker Feb 9, 2021
cc6e50b
Add strings utility methods and tests
MaxStalker Feb 9, 2021
70a6da7
Add hash utility methods and tests
MaxStalker Feb 9, 2021
9c1e518
Add file utility methods
MaxStalker Feb 9, 2021
838b280
Add exports from utils root
MaxStalker Feb 9, 2021
56f689b
Add test usage and todo
MaxStalker Feb 9, 2021
20e895b
Add .history to ignore list
MaxStalker Feb 10, 2021
c908eaa
Refactor generator. Create template for asset
MaxStalker Feb 10, 2021
247c63d
Add scripts to package.json
MaxStalker Feb 10, 2021
c086026
Expose trimAndSplit method
MaxStalker Feb 10, 2021
e89cf3f
Create environment variables with addresses for common contracts
MaxStalker Feb 10, 2021
8100f48
Clean up unused files
MaxStalker Feb 11, 2021
d74bb98
Update script names
MaxStalker Feb 11, 2021
2fe299b
Scaffold README
MaxStalker Feb 11, 2021
9753780
Remove hash from template
MaxStalker Feb 11, 2021
eabfd37
Add file utility methods
MaxStalker Feb 11, 2021
d1bdba0
Create script for generator to work with transactions folder
MaxStalker Feb 11, 2021
aaafc46
Add gitignore
MaxStalker Feb 11, 2021
53cf9cd
Update scripts. Add babel plugin for absolute paths
MaxStalker Feb 12, 2021
d0a3a68
Add statement about generated files
MaxStalker Feb 12, 2021
49798f8
Update babel config to resolve absolute paths
MaxStalker Feb 12, 2021
c3fb56e
Update jsdoc for methods
MaxStalker Feb 12, 2021
2979dec
Complete generateExports method
MaxStalker Feb 12, 2021
603013c
Refactor generator code
MaxStalker Feb 12, 2021
25e4203
Update templates
MaxStalker Feb 12, 2021
96516ad
Add generated files to ignore list
MaxStalker Feb 12, 2021
0beaa41
Export necessary modules
MaxStalker Feb 12, 2021
81c7cff
Update test use case
MaxStalker Feb 12, 2021
1ab27be
Update yarn.lock
MaxStalker Apr 23, 2021
d08d118
Update code around file path splitters
MaxStalker Apr 23, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

flow.json

# Javascript node_modules
**/**/node_modules

# IDE related files
.idea
.vscode
.history
git
20 changes: 20 additions & 0 deletions lib/javascript/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
],
"plugins": [
[
"module-resolver",
{
"root": ["./src"]
}
]
]
}
2 changes: 2 additions & 0 deletions lib/javascript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/generated
**/templates/index.js
18 changes: 18 additions & 0 deletions lib/javascript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Important Notice

Please, do NOT edit or commit files in `generated` folder. All files in that folder will be deleted
by generator on next run, meaning your work will be lost. If you want to introduce changes to those
files - edit Handlebars templates or generator code.

# Development

## First steps

- Run `yarn precompile-handlebars` to precompile Handlebars templates
- Run `yarn generate-cadence-code` to generate Javascript files, which would export Cadence code
together with other template information

## Publish

Package will be automatically published via CD/CI tools, when new changes are merged into `master`
branch, no need to do extra steps.
5 changes: 5 additions & 0 deletions lib/javascript/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
"testEnvironment": "node",
"verbose": true,
"coveragePathIgnorePatterns": ["/node_modules/"],
};
24 changes: 24 additions & 0 deletions lib/javascript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "js-asset-generator",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"test-usage": "node_modules/.bin/babel-node src/testUsage.js",
"generate-cadence-code": "node_modules/.bin/babel-node src/generator",
"precompile-handlebars": "node_modules/.bin/handlebars src/generator/templates -f ./src/generator/templates/index.js -e hbs -c handlebars"
},
"devDependencies": {
"handlebars": "^4.7.6",
"handlebars-loader": "^1.7.1",
"jest": "^26.6.3",
"prettier": "^2.2.1"
},
"dependencies": {
"@babel/core": "^7.12.13",
"@babel/node": "^7.12.13",
"@babel/preset-env": "^7.12.13",
"babel-plugin-module-resolver": "^4.1.0",
"flow-js-testing": "file:../../../flow-js-testing"
}
}
7 changes: 7 additions & 0 deletions lib/javascript/prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
printWidth: 100,
endOfLine: "lf",
proseWrap: "always",
useTabs: true,
tabWidth: 2,
};
45 changes: 45 additions & 0 deletions lib/javascript/src/generator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Handlebars from "handlebars";
import { resolve } from "path";
import {getFilesList, getSplitCharacter, trimAndSplit, underscoreToCamelCase} from "../utils";
import { clearPath, generateExports, readFile, writeFile } from '../utils/file'

// Load compiled templates
import "./templates";

const processFolder = async (input, output) => {
const splitCharacter = getSplitCharacter(input);
const fullBasePath = `${resolve(input)}${splitCharacter}`;
const fileList = await getFilesList(input);

for (let i = 0; i < fileList.length; i++) {
const path = fileList[i]
const packages = trimAndSplit(path, fullBasePath);
const pathPackages = packages.slice(0, -1);
const file = packages.slice(-1)[0];

const code = readFile(path);
const name = underscoreToCamelCase(file.replace(".cdc", ""));

const data = Handlebars.templates.asset({ code, name, assetName: name });

const templateFolder = pathPackages.join(`/`);
const filePath = `${output}/${templateFolder}/${name}.js`;

writeFile(filePath, data);
}

// Generate index.js exports in each folder
await generateExports(output, Handlebars.templates.package);
}

const main = async () => {
const templatePath = "./src/generated";

clearPath(templatePath)
await processFolder("../../transactions", "./src/generated/transactions");
await processFolder("../../contracts", "./src/generated/contracts");

console.log("✅ Done. You can find newly generated code in src/generated folder")
};

main();
26 changes: 26 additions & 0 deletions lib/javascript/src/generator/templates/asset.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { replaceImportAddresses } from "flow-js-testing/dist/utils/imports";
import { getEnvironment } from "utils/env";
import { reportMissingImports } from 'utils'

export const CODE = `
{{ code }}
`;

/**
* Method to generate cadence code for TestAsset
* @param {Object.<string, string>} addressMap - contract name as a key and address where it's deployed as value
* @param {( "emulator" | "testnet" | "mainnet" )} [env] - current working environment, defines default deployed contracts
*/
export const {{ assetName }} = (addressMap = {}, env = "testnet") => {
const envMap = getEnvironment(env);
const fullMap = {
...envMap,
...addressMap,
};

// If there are any missing imports in fullMap it will be reported via console
reportMissingImports(CODE, fullMap, `{{ name }} =>`)

return replaceImportAddresses(CODE, fullMap);
};

16 changes: 16 additions & 0 deletions lib/javascript/src/generator/templates/package.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{{#each folders ~}}
import {{this}} from './{{this}}'
{{/each}}

{{#each files ~}}
import { {{this}} } from './{{this}}'
{{/each}}

export default {
{{#each folders}}
{{ this }},
{{/each}}
{{#each files}}
{{ this }},
{{/each}}
}
1 change: 1 addition & 0 deletions lib/javascript/src/generator/templates/version.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const VERSION = {{version}}
8 changes: 8 additions & 0 deletions lib/javascript/src/testUsage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import flowToken from "./generated/transactions/flowToken";

const main = async () => {
const code = flowToken.burnTokens()
console.log({ code });
};

main();
22 changes: 22 additions & 0 deletions lib/javascript/src/utils/env/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const ENVIRONMENTS = {
emulator: {
FlowToken: "0xee82856bf20e2aa6",
FungibleToken: "0x0ae53cb6e3f42a79",
},
testnet: {
FlowToken: "0x7e60df042a9c0868",
FungibleToken: "0x9a0766d93b6608b7",
LockedTokens: "0x95e019a17d0e23d7",
StakingProxy: "0x7aad92e5a0715d21",
},
mainnet: {
FlowToken: "0x1654653399040a61",
FungibleToken: "0xf233dcee88fe0abe",
LockedTokens: "0x8d0e87b65159ae63",
StakingProxy: "0x62430cf28c26d095",
},
};

export const getEnvironment = (env) => {
return ENVIRONMENTS[env] || ENVIRONMENTS.emulator;
};
87 changes: 87 additions & 0 deletions lib/javascript/src/utils/file/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import fs from "fs";
import { resolve, dirname } from "path";
import prettier from 'prettier';
import parserBabel from 'prettier/parser-babel';
import { underscoreToCamelCase } from "utils/strings";

/**
* Syntax sugar for file reading
* @param {string} path - path to file to be read
*/
export const readFile = (path) => {
return fs.readFileSync(path, "utf8");
};

/**
* Syntax sugar for file writing
* @param {string} path - path to file to be read
* @param {string} data - data to write into file
*/
export const writeFile = (path, data) => {
const targetDir = dirname(path);
fs.mkdirSync(targetDir, { recursive: true });
return fs.writeFileSync(path, data, { encoding: "utf8" });
};

/**
* Syntax sugar for removing directory and all it's contents
* @param {string} path - path to directory to delete
*/
export const clearPath = (path) => {
fs.rmdirSync(path, { recursive: true });
};

export const getFilesList = async (dir) => {
const dirents = await fs.promises.readdir(dir, { withFileTypes: true });
const files = await Promise.all(
dirents.map((dirent) => {
const res = resolve(dir, dirent.name);
return dirent.isDirectory() ? getFilesList(res) : res;
})
);
return files.flat();
};

export const sansExtension = (fileName) => {
return fileName.replace(/\..*/, "");
};

export const prettify = (code) => {
return prettier.format(code, { parser: 'babel', plugins: [parserBabel] });
};

export const generateExports = async (dir, template) => {
const entities = await fs.promises.readdir(dir, { withFileTypes: true });

const currentFolder = entities.reduce(
(acc, entity) => {
if (entity.isDirectory()) {
acc.folders.push(entity);
acc.folderNames.push(entity.name);
} else {
const camelCased = underscoreToCamelCase(entity.name)
const fileName = sansExtension(camelCased)
acc.files.push(fileName);
}
return acc;
},
{ folderNames: [], folders: [], files: [] }
);

currentFolder.name = dir;

const packageData = template({
folders: currentFolder.folderNames,
files: currentFolder.files,
});
writeFile(`${dir}/index.js`, prettify(packageData))

await Promise.all(
currentFolder.folders.map((dirent) => {
const res = resolve(dir, dirent.name);
return dirent.isDirectory() ? generateExports(res, template) : res;
})
);

return currentFolder;
};
43 changes: 43 additions & 0 deletions lib/javascript/src/utils/generator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { extractImports } from "flow-js-testing/dist/utils/imports";

/**
* Get list of missing
* @param {string} code - template cadence code
* @param {Object.<string, string>} addressMap - contract name as a key and address where it's deployed as value
*/
export const missingImports = (code, addressMap) => {
const importsList = extractImports(code);
const missing = [];

for (const key in importsList) {
if (!addressMap[key] && importsList.hasOwnProperty(key)) {
missing.push(key);
}
}

return missing;
};

/**
* Get list of missing
* @param {string} prefix - template cadence code
* @param {Array.<string>} list - list of missing addresses
*/
export const report = (list = [], prefix = "") => {
const errorMessage = `Missing imports for contracts:`;
const message = prefix ? `${prefix} ${errorMessage}` : errorMessage;
console.error(message, list);
};

/**
* Get list of missing
* @param {string} code - template cadence code
* @param {Object.<string, string>} addressMap - contract name as a key and address where it's deployed as value
* @param {string} [prefix] - prefix to add to error message
*/
export const reportMissingImports = (code, addressMap, prefix = "") => {
const list = missingImports(code, addressMap);
if (list.length > 0) {
report(list, prefix);
}
};
8 changes: 8 additions & 0 deletions lib/javascript/src/utils/hash/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import crypto from "crypto";

export const hashInput = (input) => {
const algorithm = "sha256";
const inputEncoding = "utf8";
const encoding = "hex";
return crypto.createHash(algorithm).update(input, inputEncoding).digest(encoding);
};
11 changes: 11 additions & 0 deletions lib/javascript/src/utils/hash/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { hashInput } from "./index";

describe("test hashing", () => {
test("basic hash", () => {
const input = "test";
const result = hashInput(input);
const expected = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08";

expect(result).toBe(expected);
});
});
4 changes: 4 additions & 0 deletions lib/javascript/src/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { hashInput } from "./hash";
export { getFilesList, readFile, writeFile } from "./file";
export { underscoreToCamelCase, trimAndSplit, getSplitCharacter } from "./strings";
export { reportMissingImports, report } from "./generator";
Loading