Skip to content

feat: add create-typink package #127

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

Merged
merged 36 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
27b584d
Setup package
1cedrus Jan 22, 2025
d6789d1
Update package.json
1cedrus Jan 22, 2025
edb38d8
Add basic app flows
1cedrus Jan 22, 2025
0262fa4
Rename package, add template, wallet connector, and network options
1cedrus Jan 23, 2025
0b6c5f8
Update README.md
1cedrus Jan 23, 2025
962ce49
Add TODOs
1cedrus Jan 23, 2025
4266a53
Add prettier format task, refactor files
1cedrus Jan 23, 2025
0264478
Fix networks need to be an array
1cedrus Jan 24, 2025
415eb57
Fix yarn prettify
1cedrus Jan 24, 2025
2a721ae
Force at least one network to be choose
1cedrus Jan 24, 2025
5a3cc29
Add handler for template files
1cedrus Jan 24, 2025
12fd972
Refactoring 🔧
1cedrus Jan 24, 2025
cb587ec
run directly via tsx
sinzii Jan 25, 2025
108c3b4
Deploy contract in astar and alephzero
1cedrus Jan 25, 2025
4103ad0
Handle preset contract option
1cedrus Jan 25, 2025
da89400
Refactoring
1cedrus Jan 25, 2025
0ddb402
Handle wallet connector options
1cedrus Jan 26, 2025
e12a5b6
Fix arg not capture `--template` value
1cedrus Jan 26, 2025
e15cb13
Not create files if template render nothing
1cedrus Jan 26, 2025
ed573e4
Remove `--version` arg, `default` is default value of `--template`
1cedrus Jan 26, 2025
71923a4
Fix `bin` import
1cedrus Jan 26, 2025
d3955a8
Use `validate-npm-package-name` instead of regex
1cedrus Jan 26, 2025
a616c01
Refactoring 🔧🔧
1cedrus Jan 26, 2025
8580ddb
Fix imports
1cedrus Jan 26, 2025
7ae553f
Github Action first run expect to be failed
1cedrus Jan 26, 2025
699a09f
Update template for wallet connector options
1cedrus Jan 26, 2025
3191052
Github Action for testing
1cedrus Jan 26, 2025
cb07d1d
Refactoring 🔧
1cedrus Jan 26, 2025
4de639f
require node > v20
sinzii Jan 26, 2025
cdde2c9
Merge remote-tracking branch 'origin/feature/create-typink' into feat…
sinzii Jan 26, 2025
0445b13
revert changes
sinzii Jan 26, 2025
cfb4b1e
update dapp styling
sinzii Jan 26, 2025
098bc8a
add version/author
sinzii Jan 26, 2025
9b80685
Merge remote-tracking branch 'origin/main' into feature/create-typink
sinzii Jan 26, 2025
034af45
refactoring files
sinzii Jan 26, 2025
f6bfb7a
fix esm
sinzii Jan 26, 2025
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
60 changes: 60 additions & 0 deletions .github/workflows/create-typink-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Create Typink Tests

on:
push:
workflow_dispatch:
merge_group:

jobs:
create-typink-tests:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.x]

steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
- run: yarn install --immutable
- run: yarn build
- name: Create app with create-typink (Default)
run: |
export YARN_ENABLE_IMMUTABLE_INSTALLS=false
cd ..
node ./typink/packages/create-typink/dist/bin/create-typink.mjs --no-git -n typink-app-default -t default -p greeter -N "Pop Testnet" -N "Aleph Zero Testnet" -w Default
ls -la
cd ./typink-app-default
ls -la
- name: Try to build (Default)
run: |
cd ../typink-app-default
yarn build
- name: Create app with create-typink (SubConnect V2)
run: |
export YARN_ENABLE_IMMUTABLE_INSTALLS=false
cd ..
node ./typink/packages/create-typink/dist/bin/create-typink.mjs --no-git -n typink-app-subconnect-v2 -t default -p greeter -N "Pop Testnet" -N "Aleph Zero Testnet" -w "SubConnect V2"
ls -la
cd ./typink-app-subconnect-v2
ls -la
- name: Try to build (SubConnect V2)
run: |
cd ../typink-app-subconnect-v2
yarn build
- name: Create app with create-typink (Talisman Connect)
run: |
export YARN_ENABLE_IMMUTABLE_INSTALLS=false
cd ..
node ./typink/packages/create-typink/dist/bin/create-typink.mjs --no-git -n typink-app-talisman-connect -t default -p greeter -N "Pop Testnet" -N "Aleph Zero Testnet" -w "Talisman Connect"
ls -la
cd ./typink-app-talisman-connect
ls -la
- name: Try to build (Talisman Connect)
run: |
cd ../typink-app-talisman-connect
yarn build
26 changes: 25 additions & 1 deletion examples/demo/src/contracts/deployments.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { alephZeroTestnet, ContractDeployment, popTestnet } from 'typink';
import { alephZeroTestnet, ContractDeployment, popTestnet, alephZero, astar } from 'typink';
import greeterMetadata from './artifacts/greeter/greeter.json';
import psp22Metadata from './artifacts/psp22/psp22.json';

Expand All @@ -22,6 +22,18 @@ export const greeterDeployments: ContractDeployment[] = [
network: alephZeroTestnet.id,
address: '5CDia8Y46K7CbD2vLej2SjrvxpfcbrLVqK2He3pTJod2Eyik',
},
{
id: ContractId.GREETER,
metadata: greeterMetadata as any,
network: alephZero.id,
address: '5CYZtKBxuva33JREQkbeaE4ed2niWb1ijS4pgXbFD61yZti1',
},
{
id: ContractId.GREETER,
metadata: greeterMetadata as any,
network: astar.id,
address: 'WejJavPYsGgcY8Dr5KQSJrTssxUh5EbeYiCfdddeo5aTbse',
},
];

export const psp22Deployments: ContractDeployment[] = [
Expand All @@ -37,6 +49,18 @@ export const psp22Deployments: ContractDeployment[] = [
network: alephZeroTestnet.id,
address: '5G5moUCkx5E2TD3CcRWvweg7rpCLngRmwukuKdaohvfBBmXr',
},
{
id: ContractId.PSP22,
metadata: psp22Metadata as any,
network: alephZero.id,
address: '5EkDPuyLdubc4uUmEhzMFRtcNtmxSoUecfcc9wWLUR7ZFXbb',
},
{
id: ContractId.PSP22,
metadata: psp22Metadata as any,
network: astar.id,
address: 'YFhgALp2qPtPe1pTFereqqB4RUXKzVM7YtCYfqQX412GWHr',
},
];

export const deployments = [...greeterDeployments, ...psp22Deployments];
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"vitest": "^2.1.8"
},
"engines": {
"node": ">=18"
"node": ">=20"
},
"license": "MIT"
}
21 changes: 21 additions & 0 deletions packages/create-typink/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Thang X. Vu <thang@dedot.dev>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
3 changes: 3 additions & 0 deletions packages/create-typink/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# create-typink

A CLI tool to create a new project using Typink
4 changes: 4 additions & 0 deletions packages/create-typink/bin/create-typink.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node
import { createTypink } from '../index.js';

createTypink();
31 changes: 31 additions & 0 deletions packages/create-typink/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "create-typink",
"version": "0.0.0",
"author": "Tung Vu <tung@dedot.dev>",
"main": "src/index.ts",
"type": "module",
"scripts": {
"build": "tsc --project tsconfig.build.json && cp -R ./bin ./dist && cp -R ./src/templates ./dist",
"clean": "rm -rf ./dist && rm -rf ./tsconfig.tsbuildinfo ./tsconfig.build.tsbuildinfo"
},
"bin": "./bin/create-typink.mjs",
"dependencies": {
"arg": "^5.0.2",
"chalk": "^5.4.1",
"ejs": "^3.1.10",
"execa": "^9.5.2",
"inquirer": "^12.3.2",
"listr2": "^8.2.5",
"prettier": "^3.4.2",
"validate-npm-package-name": "^6.0.0"
},
"publishConfig": {
"access": "public",
"directory": "dist"
},
"license": "MIT",
"devDependencies": {
"@types/ejs": "^3",
"@types/validate-npm-package-name": "^4.0.2"
}
}
52 changes: 52 additions & 0 deletions packages/create-typink/src/createProject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Options } from './types.js';
import { Listr } from 'listr2';
import { fileURLToPath } from 'url';
import * as path from 'path';
import chalk from 'chalk';
import {
createProjectDirectory,
createFirstCommit,
copyTemplateFiles,
prettierFormat,
installPackages,
} from './tasks/index.js';

export async function createProject(options: Options) {
const { projectName, skipInstall, noGit } = options;

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const templateDirectory = path.resolve(__dirname, './templates');
const targetDirectory = path.resolve(process.cwd(), projectName!);

const tasks = new Listr(
[
{
title: `📁 Create project directory ${targetDirectory}`,
task: () => createProjectDirectory(projectName!),
},
{
title: `🚀 Creating a new Typink app in ${chalk.green.bold(projectName)}`,
task: () => copyTemplateFiles(options, templateDirectory, targetDirectory),
},
{
title: '📦 Installing dependencies with yarn, this could take a while',
task: () => installPackages(targetDirectory),
skip: skipInstall,
},
{
title: '🧹 Formatting the code with Prettier',
task: () => prettierFormat(targetDirectory, options),
},
{
title: `🚨 Create the very first Git commit`,
task: () => createFirstCommit(targetDirectory),
skip: noGit,
},
],
{ rendererOptions: { suffixSkips: true }, exitOnError: true },
);

await tasks.run();
}
38 changes: 38 additions & 0 deletions packages/create-typink/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import chalk from 'chalk';
import {
parseArguments,
renderIntroArt,
promptMissingOptions,
renderHelpMessage,
renderOutroMessage,
} from './utils/index.js';
import { createProject } from './createProject.js';
import { fileURLToPath } from 'url';

export async function createTypink() {
try {
renderIntroArt();

const args = parseArguments();

if (args.help) {
renderHelpMessage();
return;
}

const options = await promptMissingOptions(args);

await createProject(options);

renderOutroMessage(options);
} catch (error) {
console.error(chalk.red.bold('🚨 An error occurred:'), error);
console.error(chalk.red.bold('🚨 Sorry, exiting...'));
}
}

// run directly from root folder: tsx ./packages/create-typink/src/index.ts
const __filename = fileURLToPath(import.meta.url);
if (process.argv[1] === __filename) {
createTypink().catch(console.error);
}
83 changes: 83 additions & 0 deletions packages/create-typink/src/tasks/copyTemplateFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { execa } from 'execa';
import { Options } from '../types.js';
import * as fs from 'fs';
import * as ejs from 'ejs';
import * as path from 'path';
import { stringCamelCase } from '@dedot/utils';
import { IS_IGNORE_FILES, IS_TEMPLATE_FILE } from '../utils/index.js';

export async function copyTemplateFiles(options: Options, templatesDir: string, targetDir: string) {
const { projectName, noGit, template } = options;

const templateDir = `${templatesDir}/${template}`;

if (!fs.existsSync(templateDir)) {
throw new Error(`Template directory not found: ${templateDir}`);
}

await fs.promises.cp(templateDir, targetDir, { recursive: true });

const packageJsonPath = `${targetDir}/package.json`;
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));

packageJson.name = projectName;
await fs.promises.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

processPresetContract(options, targetDir);
processTemplateFiles(options, targetDir);

if (!noGit) {
await execa('git', ['init'], { cwd: targetDir });
await execa('git', ['checkout', '-b', 'main'], { cwd: targetDir });
}
}

export async function processPresetContract(options: Options, targetDir: string) {
const dirsToCheck = [`${targetDir}/contracts/artifacts`, `${targetDir}/contracts/types`];

dirsToCheck.forEach(async (dir) => {
for (const file of await fs.promises.readdir(dir, { withFileTypes: true })) {
if (file.name === options.presetContract) {
continue;
}

await fs.promises.rm(path.join(dir, file.name), { recursive: true });
}
});
}

export async function processTemplateFiles(rawOptions: Options, targetDir: string) {
const options = {
...rawOptions,
networks: rawOptions.networks?.map(stringCamelCase),
};

await processTemplateFilesRecursive(options, targetDir);
}

async function processTemplateFilesRecursive(options: any, dir: string) {
if (IS_IGNORE_FILES.test(dir)) {
return;
}

const files = await fs.promises.readdir(dir, { withFileTypes: true });

for (const file of files) {
const filePath = path.join(dir, file.name);

if (file.isDirectory()) {
await processTemplateFilesRecursive(options, filePath);
} else {
if (IS_TEMPLATE_FILE.test(filePath)) {
const content = fs.readFileSync(filePath, 'utf-8');
const result = ejs.render(content, { options });

if (result.trim() !== '') {
await fs.promises.writeFile(filePath.replace('.template.ejs', ''), result);
}

await fs.promises.rm(filePath);
}
}
}
}
8 changes: 8 additions & 0 deletions packages/create-typink/src/tasks/createFirstCommit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { execa } from 'execa';

export async function createFirstCommit(targetDirectory: string) {
await execa('git', ['add', '.'], { cwd: targetDirectory });
await execa('git', ['commit', '-m', 'Initial commit 🚀'], {
cwd: targetDirectory,
});
}
13 changes: 13 additions & 0 deletions packages/create-typink/src/tasks/createProjectDirectory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { execa } from 'execa';

export async function createProjectDirectory(projectName: string) {
try {
const result = await execa('mkdir', [projectName]);

if (result.failed) {
throw new Error(`There was an error when running mkdir command`);
}
} catch (error: any) {
throw new Error(`Failed to create project directory: ${projectName} with error: ${error.message}`);
}
}
5 changes: 5 additions & 0 deletions packages/create-typink/src/tasks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './createProjectDirectory.js';
export * from './copyTemplateFiles.js';
export * from './createFirstCommit.js';
export * from './installPackages.js';
export * from './prettierFormat.js';
5 changes: 5 additions & 0 deletions packages/create-typink/src/tasks/installPackages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { execa } from 'execa';

export async function installPackages(targetDirectory: string) {
await execa('yarn', ['install'], { cwd: targetDirectory });
}
Loading
Loading