-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add custom workspace scripts with anchor-build option
- Loading branch information
Showing
9 changed files
with
252 additions
and
20 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 |
---|---|---|
@@ -1,9 +1,11 @@ | ||
#!/usr/bin/env node | ||
import { intro, log, note, outro } from '@clack/prompts' | ||
import { createWorkspace } from 'create-nx-workspace' | ||
import { intro, log, note, outro, spinner } from '@clack/prompts' | ||
import { execAndWait } from 'create-nx-workspace/src/utils/child-process-utils' | ||
import { detectInvokedPackageManager } from 'create-nx-workspace/src/utils/package-manager' | ||
import { getAppInfo } from '../lib/get-app-info' | ||
import { getArgs } from '../lib/get-args' | ||
import { customCreateWorkspace } from '../lib/custom-create-workspace' | ||
import { join } from 'node:path' | ||
|
||
async function main() { | ||
const app = getAppInfo() | ||
|
@@ -12,25 +14,43 @@ async function main() { | |
const args = await getArgs(process.argv, pm) | ||
|
||
if (!args.dryRun) { | ||
log.message(`Creating workspace...\n`) | ||
const { directory } = await createWorkspace(`${args.package}`, { | ||
anchor: args.anchor, | ||
anchorName: args.anchorName, | ||
appName: args.appName, | ||
commit: { | ||
name: 'Solana Developers', | ||
email: '[email protected]', | ||
message: 'chore: initial commit', | ||
const s = spinner() | ||
s.start(`Creating workspace...`) | ||
const { directory } = await customCreateWorkspace( | ||
`${args.package}`, | ||
{ | ||
anchor: args.anchor, | ||
anchorName: args.anchorName, | ||
appName: args.appName, | ||
commit: { | ||
name: 'Solana Developers', | ||
email: '[email protected]', | ||
message: 'chore: initial commit', | ||
}, | ||
dryRun: args.dryRun, | ||
name: args.name, | ||
nxCloud: false, | ||
packageManager: args.packageManager, | ||
ui: args.ui, | ||
}, | ||
dryRun: args.dryRun, | ||
name: args.name, | ||
nxCloud: false, | ||
packageManager: args.packageManager, | ||
ui: args.ui, | ||
}) | ||
async () => { | ||
s.stop('Workspace created.') | ||
if (args.anchor === 'none' || !args.anchorBuild) { | ||
s.message('No anchor template to build.') | ||
return | ||
} | ||
s.start('Building anchor...') | ||
try { | ||
await execAndWait(`npx nx run ${args.anchorName}:build`, join(args.name)) | ||
s.stop('Anchor built.') | ||
} catch (e) { | ||
s.stop('Anchor build failed.') | ||
log.warn('Do you have Rust and Anchor installed?') | ||
} | ||
}, | ||
) | ||
|
||
log.success('Workspace created.') | ||
outro(`Run \`cd ${directory}\` to get started.`) | ||
outro(`Run \`cd ${directory.replace(process.cwd(), '.')}\` to get started.`) | ||
} else { | ||
note(JSON.stringify(args, null, 2), 'Dry run, no changes were made.') | ||
outro(`Would have created the workspace: ${args.name} with preset: ${args.preset}.`) | ||
|
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 |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
exports[`get-args expected args should get args with minimal parameters 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": false, | ||
"anchorName": "anchor", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
@@ -17,6 +18,7 @@ exports[`get-args expected args should get args with minimal parameters 1`] = ` | |
exports[`get-args expected args should get args with minimal parameters and package manager npm 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": false, | ||
"anchorName": "anchor", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
@@ -31,6 +33,7 @@ exports[`get-args expected args should get args with minimal parameters and pack | |
exports[`get-args expected args should get args with minimal parameters and package manager pnpm 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": false, | ||
"anchorName": "anchor", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
@@ -45,6 +48,7 @@ exports[`get-args expected args should get args with minimal parameters and pack | |
exports[`get-args expected args should get args with minimal parameters and package manager yarn 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": false, | ||
"anchorName": "anchor", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
@@ -59,6 +63,7 @@ exports[`get-args expected args should get args with minimal parameters and pack | |
exports[`get-args expected args should get args with preset: --preset next --ui tailwind --anchor counter 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": false, | ||
"anchorName": "anchor", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
@@ -73,6 +78,7 @@ exports[`get-args expected args should get args with preset: --preset next --ui | |
exports[`get-args expected args should get args with preset: --preset react --anchor counter --anchor-name program --ui tailwind 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": false, | ||
"anchorName": "program", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
@@ -87,6 +93,7 @@ exports[`get-args expected args should get args with preset: --preset react --an | |
exports[`get-args expected args should get args with preset: --preset react --anchor none --ui none 1`] = ` | ||
{ | ||
"anchor": "none", | ||
"anchorBuild": false, | ||
"anchorName": "anchor", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
@@ -101,6 +108,7 @@ exports[`get-args expected args should get args with preset: --preset react --an | |
exports[`get-args expected args should get args with preset: --preset react --ui tailwind --anchor counter 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": false, | ||
"anchorName": "anchor", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
@@ -112,9 +120,25 @@ exports[`get-args expected args should get args with preset: --preset react --ui | |
} | ||
`; | ||
|
||
exports[`get-args expected args should get args with preset: --preset=react --anchor=counter --anchor-name=program --anchor-build --ui=tailwind 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": true, | ||
"anchorName": "program", | ||
"appName": "web", | ||
"dryRun": false, | ||
"name": "my-app", | ||
"package": "@solana-developers/[email protected]", | ||
"packageManager": "npm", | ||
"preset": "react", | ||
"ui": "tailwind", | ||
} | ||
`; | ||
|
||
exports[`get-args expected args should get args with preset: --preset=react --anchor=counter --anchor-name=program --ui=tailwind 1`] = ` | ||
{ | ||
"anchor": "counter", | ||
"anchorBuild": false, | ||
"anchorName": "program", | ||
"appName": "web", | ||
"dryRun": false, | ||
|
78 changes: 78 additions & 0 deletions
78
packages/create-solana-dapp/lib/custom-create-empty-workspace.ts
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,78 @@ | ||
import { output } from '@nx/devkit' | ||
import { CreateWorkspaceOptions } from 'create-nx-workspace' | ||
import { execAndWait } from 'create-nx-workspace/src/utils/child-process-utils' | ||
import { mapErrorToBodyLines } from 'create-nx-workspace/src/utils/error-utils' | ||
import { | ||
getPackageManagerCommand, | ||
getPackageManagerVersion, | ||
PackageManager, | ||
} from 'create-nx-workspace/src/utils/package-manager' | ||
import { getFileName } from 'create-nx-workspace/src/utils/string-utils' | ||
import { unparse } from 'create-nx-workspace/src/utils/unparse' | ||
import { join } from 'path' | ||
|
||
/** | ||
* Create a new Nx workspace | ||
* @param tmpDir temporary directory to invoke nx cli | ||
* @param name name of new nx workspace | ||
* @param packageManager current package manager | ||
* @param options options to pass to nx cli | ||
* @returns | ||
*/ | ||
export async function customCreateEmptyWorkspace<T extends CreateWorkspaceOptions>( | ||
tmpDir: string, | ||
name: string, | ||
packageManager: PackageManager, | ||
options: T, | ||
): Promise<string> { | ||
// Ensure to use packageManager for args | ||
// if it's not already passed in from previous process | ||
if (!options.packageManager) { | ||
options.packageManager = packageManager | ||
} | ||
|
||
options.name = getFileName(name) | ||
const directory = options.name | ||
|
||
const args = unparse({ | ||
...options, | ||
}).join(' ') | ||
|
||
const pmc = getPackageManagerCommand(packageManager) | ||
|
||
const command = `new ${args}` | ||
|
||
const workingDir = process.cwd().replace(/\\/g, '/') | ||
let nxWorkspaceRoot = `"${workingDir}"` | ||
|
||
// If path contains spaces there is a problem in Windows for npm@6. | ||
// In this case we have to escape the wrapping quotes. | ||
if (process.platform === 'win32' && /\s/.test(nxWorkspaceRoot) && packageManager === 'npm') { | ||
const pmVersion = +getPackageManagerVersion(packageManager, tmpDir).split('.')[0] | ||
if (pmVersion < 7) { | ||
nxWorkspaceRoot = `\\"${nxWorkspaceRoot.slice(1, -1)}\\"` | ||
} | ||
} | ||
// let workspaceSetupSpinner = ora(`Creating your workspace in ${directory}`).start() | ||
|
||
try { | ||
const fullCommand = `${pmc.exec} nx ${command} --nxWorkspaceRoot=${nxWorkspaceRoot}` | ||
await execAndWait(fullCommand, tmpDir) | ||
|
||
// workspaceSetupSpinner.succeed(`Successfully created the workspace: ${directory}.`) | ||
} catch (e) { | ||
// workspaceSetupSpinner.fail() | ||
if (e instanceof Error) { | ||
output.error({ | ||
title: `Failed to create a workspace.`, | ||
bodyLines: mapErrorToBodyLines(e), | ||
}) | ||
} else { | ||
console.error(e) | ||
} | ||
process.exit(1) | ||
} finally { | ||
// workspaceSetupSpinner.stop() | ||
} | ||
return join(workingDir, directory) | ||
} |
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,54 @@ | ||
import { output } from '@nx/devkit' | ||
import { execAndWait } from 'create-nx-workspace/src/utils/child-process-utils' | ||
import { mapErrorToBodyLines } from 'create-nx-workspace/src/utils/error-utils' | ||
import { | ||
generatePackageManagerFiles, | ||
getPackageManagerCommand, | ||
PackageManager, | ||
} from 'create-nx-workspace/src/utils/package-manager' | ||
import { writeFileSync } from 'fs' | ||
import { nxVersion } from 'nx/src/utils/versions' | ||
import { join } from 'path' | ||
import { dirSync } from 'tmp' | ||
|
||
/** | ||
* Creates a temporary directory and installs Nx in it. | ||
* @param packageManager package manager to use | ||
* @returns directory where Nx is installed | ||
*/ | ||
export async function customCreateSandbox(packageManager: PackageManager) { | ||
const { install, preInstall } = getPackageManagerCommand(packageManager) | ||
|
||
const tmpDir = dirSync().name | ||
try { | ||
writeFileSync( | ||
join(tmpDir, 'package.json'), | ||
JSON.stringify({ | ||
dependencies: { | ||
nx: nxVersion, | ||
'@nx/workspace': nxVersion, | ||
}, | ||
license: 'MIT', | ||
}), | ||
) | ||
generatePackageManagerFiles(tmpDir, packageManager) | ||
|
||
if (preInstall) { | ||
await execAndWait(preInstall, tmpDir) | ||
} | ||
|
||
await execAndWait(install, tmpDir) | ||
} catch (e) { | ||
if (e instanceof Error) { | ||
output.error({ | ||
title: `Failed to install dependencies`, | ||
bodyLines: mapErrorToBodyLines(e), | ||
}) | ||
} else { | ||
console.error(e) | ||
} | ||
process.exit(1) | ||
} | ||
|
||
return tmpDir | ||
} |
50 changes: 50 additions & 0 deletions
50
packages/create-solana-dapp/lib/custom-create-workspace.ts
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,50 @@ | ||
import { CreateWorkspaceOptions } from 'create-nx-workspace' | ||
import { createPreset } from 'create-nx-workspace/src/create-preset' | ||
import { mapErrorToBodyLines } from 'create-nx-workspace/src/utils/error-utils' | ||
import { initializeGitRepo } from 'create-nx-workspace/src/utils/git/git' | ||
import { getThirdPartyPreset } from 'create-nx-workspace/src/utils/preset/get-third-party-preset' | ||
import { customCreateEmptyWorkspace } from './custom-create-empty-workspace' | ||
import { customCreateSandbox } from './custom-create-sandbox' | ||
|
||
export async function customCreateWorkspace<T extends CreateWorkspaceOptions>( | ||
preset: string, | ||
options: T, | ||
afterCreatePreset: (options: T) => Promise<void>, | ||
) { | ||
const { packageManager, name, skipGit = false, defaultBase = 'main', commit } = options | ||
|
||
const tmpDir = await customCreateSandbox(packageManager) | ||
|
||
// nx new requires preset currently. We should probably make it optional. | ||
const directory = await customCreateEmptyWorkspace<T>(tmpDir, name, packageManager, { ...options, preset }) | ||
|
||
// If the preset is a third-party preset, we need to call createPreset to install it | ||
// For first-party presets, it will created by createEmptyWorkspace instead. | ||
// In createEmptyWorkspace, it will call `nx new` -> `@nx/workspace newGenerator` -> `@nx/workspace generatePreset`. | ||
|
||
const thirdPartyPreset = await getThirdPartyPreset(preset) | ||
if (thirdPartyPreset) { | ||
// Hide the stdout of createPreset | ||
await createPreset(thirdPartyPreset, options, packageManager, directory) | ||
} | ||
await afterCreatePreset(options) | ||
|
||
if (!skipGit && commit) { | ||
try { | ||
await initializeGitRepo(directory, { defaultBase, commit }) | ||
} catch (e) { | ||
if (e instanceof Error) { | ||
console.error({ | ||
title: 'Could not initialize git repository', | ||
bodyLines: mapErrorToBodyLines(e), | ||
}) | ||
} else { | ||
console.error(e) | ||
} | ||
} | ||
} | ||
|
||
return { | ||
directory, | ||
} | ||
} |
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
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