-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(all): Improve blueprint integration (#7)
Closes #6
- Loading branch information
Showing
13 changed files
with
2,141 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,3 +46,6 @@ jobs: | |
|
||
- name: Run ESLint | ||
run: yarn lint | ||
|
||
- name: Run tests | ||
run: yarn test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
## Building and testing locally | ||
1. Build this plugin: | ||
``` | ||
yarn build | ||
``` | ||
2. Create a new Blueprint Tact project: | ||
``` | ||
npm create ton@latest | ||
cd <project name> | ||
``` | ||
3. Add this plugin from the project's directory: | ||
``` | ||
yarn add file:/path/to/blueprint-misti | ||
``` | ||
4. Add the Blueprint configuration to the project: | ||
```bash | ||
echo "import { MistiPlugin } from '@nowarp/blueprint-misti'; | ||
export const config = { | ||
plugins: [ | ||
new MistiPlugin(), | ||
], | ||
};" > blueprint.config.ts | ||
``` | ||
5. Test the plugin calling the following command in the project directory: | ||
```bash | ||
yarn blueprint misti <options if needed> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
"words": [ | ||
"nowarp", | ||
"Georgiy", | ||
"compilables", | ||
"Komarov" | ||
], | ||
"flagWords": [], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module.exports = { | ||
preset : "ts-jest", | ||
testEnvironment : "node", | ||
testPathIgnorePatterns : [ "/node_modules/", "/dist/" ], | ||
maxWorkers : 1, | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/** | ||
* Various utilities to work with Blueprint internals and its generated files. | ||
* | ||
* @packageDocumentation | ||
*/ | ||
|
||
import { Args } from "@ton/blueprint"; | ||
import { | ||
getCompilablesDirectory, | ||
COMPILE_END, | ||
} from "@ton/blueprint/dist/compile/compile"; | ||
import { CompilerConfig } from "@ton/blueprint/dist/compile/CompilerConfig"; | ||
import { ConfigProject } from "@tact-lang/compiler"; | ||
import path from "path"; | ||
|
||
/** | ||
* Tact project info parsed from the Blueprint compilation wrapper. | ||
*/ | ||
export type TactProjectInfo = { | ||
projectName: string; | ||
target: string; | ||
options?: ConfigProject["options"]; | ||
}; | ||
|
||
/** | ||
* Extracts the `CompilerConfig` from the given project name. | ||
* | ||
* XXX: Imported from blueprint, since the original function is private: | ||
* https://github.com/ton-org/blueprint/issues/151 | ||
*/ | ||
async function getCompilerConfigForContract( | ||
name: string, | ||
): Promise<CompilerConfig> { | ||
const compilablesDirectory = await getCompilablesDirectory(); | ||
const mod = await import(path.join(compilablesDirectory, name + COMPILE_END)); | ||
if (typeof mod.compile !== "object") { | ||
throw new Error(`Object 'compile' is missing`); | ||
} | ||
return mod.compile; | ||
} | ||
|
||
/** | ||
* Extracts an information from the TypeScript wrapper file generated by Blueprint. | ||
*/ | ||
export async function extractProjectInfo( | ||
blueprintCompilePath: string, | ||
): Promise<TactProjectInfo> | never { | ||
const filePath = path.resolve(__dirname, blueprintCompilePath); | ||
const projectName = path.basename(filePath).replace(".compile.ts", ""); | ||
const compilerConfig = await getCompilerConfigForContract(projectName); | ||
switch (compilerConfig.lang) { | ||
case "func": | ||
throw new Error( | ||
"FunC projects are not currently supported: https://github.com/nowarp/misti/issues/56", | ||
); | ||
case "tact": | ||
return { | ||
projectName, | ||
target: compilerConfig.target, | ||
options: compilerConfig.options, | ||
}; | ||
default: | ||
// XXX: It might be *anything* according to the Blueprint API | ||
throw new Error(`Please specify \`lang\` property in ${filePath}`); | ||
} | ||
} | ||
|
||
/** | ||
* Converts Blueprint arguments to a list of strings. | ||
*/ | ||
export function argsToStringList(args: Args): string[] { | ||
const argsList: string[] = args._; | ||
Object.entries(args).forEach(([key, value]) => { | ||
if (key !== "_" && value !== undefined) { | ||
if (typeof value === "boolean") { | ||
argsList.push(key); | ||
} else { | ||
argsList.push(key, value.toString()); | ||
} | ||
} | ||
}); | ||
return argsList; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { Args, UIProvider } from "@ton/blueprint"; | ||
import { findCompiles, selectFile } from "@ton/blueprint/dist/utils"; | ||
import { Sym } from "./util"; | ||
import { | ||
TactProjectInfo, | ||
extractProjectInfo, | ||
argsToStringList, | ||
} from "./blueprint"; | ||
import { | ||
MistiResult, | ||
runMistiCommand, | ||
createMistiCommand, | ||
} from "@nowarp/misti/dist/cli"; | ||
import { setStdlibPath } from "./stdlibPaths"; | ||
import fs from "fs"; | ||
import path from "path"; | ||
import os from "os"; | ||
|
||
/** | ||
* Interactively selects one of the Tact projects available in the Blueprint compile wrapper. | ||
*/ | ||
async function selectProject( | ||
ui: UIProvider, | ||
args: Args, | ||
): Promise<TactProjectInfo> | never { | ||
const result = await selectFile(await findCompiles(), { | ||
ui, | ||
hint: args._.length > 1 && args._[1].length > 0 ? args._[1] : undefined, | ||
import: false, | ||
}); | ||
if (!fs.existsSync(result.path)) { | ||
throw new Error( | ||
[ | ||
`${Sym.ERR} Cannot access ${result.path}`, | ||
"Please specify path to your contract directly: `yarn blueprint misti path/to/contract.tact`", | ||
].join("\n"), | ||
); | ||
} | ||
return await extractProjectInfo(result.path); | ||
} | ||
|
||
export class MistiExecutor { | ||
private constructor( | ||
private projectName: string, | ||
private args: string[], | ||
private ui: UIProvider, | ||
) {} | ||
public static async fromArgs( | ||
args: Args, | ||
ui: UIProvider, | ||
): Promise<MistiExecutor> | never { | ||
let argsStr = argsToStringList(args).slice(1); | ||
const command = createMistiCommand(); | ||
|
||
let tactPathIsDefined = true; | ||
const originalArgsStr = [...argsStr]; | ||
try { | ||
await command.parseAsync(argsStr, { from: "user" }); | ||
} catch (error) { | ||
tactPathIsDefined = false; | ||
if (error instanceof Error && error.message.includes("is required")) { | ||
const tempPath = "/tmp/contract.tact"; | ||
argsStr.push(tempPath); | ||
await command.parseAsync(argsStr, { from: "user" }); | ||
} else { | ||
throw error; | ||
} | ||
} | ||
argsStr = originalArgsStr; | ||
|
||
if (tactPathIsDefined) { | ||
// The path to the Tact configuration or contract is explicitly specified | ||
// in arguments (e.g. yarn blueprint misti path/to/contract.tact). | ||
const tactPath = command.args[0]; | ||
const projectName = path.basename(tactPath).split(".")[0]; | ||
return new MistiExecutor(projectName, argsStr, ui); | ||
} | ||
|
||
// Interactively select the project | ||
const project = await selectProject(ui, args); | ||
try { | ||
const tactPath = this.generateTactConfig(project, "."); | ||
argsStr.push(tactPath); | ||
return new MistiExecutor(project.projectName, argsStr, ui); | ||
} catch { | ||
throw new Error(`Cannot create a Tact config in current directory`); | ||
} | ||
} | ||
|
||
/** | ||
* Generates the Tact configuration file based on the Blueprint compilation output. | ||
* | ||
* @param outDir Directory to save the generated file | ||
* @throws If it is not possible to create a path | ||
* @returns Absolute path to the generated config | ||
*/ | ||
private static generateTactConfig( | ||
config: TactProjectInfo, | ||
outDir: string, | ||
): string | never { | ||
const project: any = { | ||
name: config.projectName, | ||
path: config.target, | ||
output: path.join(os.tmpdir(), "tact-output"), | ||
}; | ||
if (config.options !== undefined) { | ||
project.options = config.options; | ||
} | ||
const content = JSON.stringify({ | ||
projects: [project], | ||
}); | ||
const outPath = path.join(outDir, "tact.config.json"); | ||
fs.writeFileSync(outPath, content); | ||
return outPath; | ||
} | ||
|
||
public async execute(): Promise<MistiResult> { | ||
this.ui.write(`${Sym.WAIT} Checking ${this.projectName}...\n`); | ||
setStdlibPath(this.args); | ||
// ! is safe: it could not be undefined in Misti 0.4+ | ||
const result = (await runMistiCommand(this.args))!; | ||
return result[1]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.