Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/sixty-roses-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'create-solana-dapp': patch
---

clean up init script utils
75 changes: 14 additions & 61 deletions src/utils/create-app-task-run-init-script.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,41 @@
import { log } from '@clack/prompts'
import { join } from 'node:path'
import { ensureTargetPath } from './ensure-target-path'
import { GetArgsResult } from './get-args-result'
import { deleteInitScript, getInitScript, InitScript } from './get-init-script'
import { getPackageJson } from './get-package-json'
import { initScriptDelete } from './init-script-delete'
import { initScriptInstructions } from './init-script-instructions'
import { initScriptRename } from './init-script-rename'
import { initScriptKey } from './init-script-schema'
import { initScriptVersion } from './init-script-version'
import { searchAndReplace } from './search-and-replace'
import { Task, taskFail } from './vendor/clack-tasks'
import { namesValues } from './vendor/names'

export function createAppTaskRunInitScript(args: GetArgsResult): Task {
return {
enabled: !args.skipInit,
title: 'Running init script',
task: async (result) => {
try {
const init = getInitScript(args.targetDirectory)
const { contents } = getPackageJson(args.targetDirectory)
const init = contents[initScriptKey]
if (!init) {
return result({ message: 'Repository does not have an init script' })
return result({ message: 'Init script not found' })
}
if (args.verbose) {
log.warn(`Running init script`)
log.warn(`Init script started`)
}

await initScriptVersion(init.versions, args.verbose)
if (args.verbose) {
log.warn(`initCheckVersion done`)
}
await initRename(args, init, args.verbose)
if (args.verbose) {
log.warn(`initRename done`)
}

const instructions: string[] = (initInstructions(init) ?? [])
await initScriptRename(args, init.rename, args.verbose)

const instructions: string[] = initScriptInstructions(init.instructions, args.verbose)
?.filter(Boolean)
.map((msg) => msg.replace('{pm}', args.packageManager))

if (args.verbose) {
log.warn(`initInstructions done`)
}
deleteInitScript(args.targetDirectory)
if (args.verbose) {
log.warn(`deleteInitScript done`)
}
return result({ message: 'Executed init script', instructions })
initScriptDelete(args)
return result({ message: 'Init script done', instructions })
} catch (error) {
taskFail(`init: Error running init script: ${error}`)
taskFail(`Error running init script: ${error}`)
}
},
}
}

async function initRename(args: GetArgsResult, init: InitScript, verbose: boolean) {
const { contents } = getPackageJson(args.targetDirectory)
// Rename template from package.json to project name throughout the whole project
if (contents.name) {
await searchAndReplace(args.targetDirectory, [contents.name], [args.name], false, verbose)
}

// Return early if there are no renames defined in the init script
if (!init?.rename) {
return
}

// Loop through each word in the rename object
for (const from of Object.keys(init.rename)) {
// Get the 'to' property from the rename object
const to = init.rename[from].to.replace('{{name}}', args.name.replace(/-/g, ''))

// Get the name matrix for the 'from' and the 'to' value
const fromNames = namesValues(from)
const toNames = namesValues(to)

for (const path of init.rename[from].paths) {
const targetPath = join(args.targetDirectory, path)
if (!(await ensureTargetPath(targetPath))) {
console.error(`init-script.rename: target does not exist ${targetPath}`)
continue
}
await searchAndReplace(join(args.targetDirectory, path), fromNames, toNames, args.dryRun)
}
}
}

function initInstructions(init: InitScript) {
return init?.instructions?.length === 0 ? [] : init?.instructions
}
2 changes: 1 addition & 1 deletion src/utils/create-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function createApp(args: GetArgsResult) {
createAppTaskCloneTemplate(args),
// Install the dependencies
createAppTaskInstallDependencies(args),
// Run the init script define in package.json .init property
// Run the (optional) init script defined in package.json
createAppTaskRunInitScript(args),
// Initialize git repository
createAppTaskInitializeGit(args),
Expand Down
51 changes: 0 additions & 51 deletions src/utils/get-init-script.ts

This file was deleted.

2 changes: 2 additions & 0 deletions src/utils/get-package-json.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { existsSync, readFileSync } from 'node:fs'
import { z } from 'zod'
import { getPackageJsonPath } from './get-package-json-path'
import { initScriptKey, InitScriptSchema } from './init-script-schema'

export function getPackageJson(targetDirectory: string): { path: string; contents: PackageJson } {
const path = getPackageJsonPath(targetDirectory)
Expand All @@ -26,6 +27,7 @@ const PackageJsonSchema = z
.object({
name: z.string().optional(),
scripts: z.record(z.string()).optional(),
[initScriptKey]: InitScriptSchema.optional(),
})
.passthrough()

Expand Down
37 changes: 37 additions & 0 deletions src/utils/get-version-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const commandMap: Record<string, { command: string; name: string; regex: RegExp }> = {
adb: {
command: 'adb --version',
name: 'Adb ',
regex: /Version (\d+\.\d+\.\d+)/,
},
anchor: {
command: 'anchor --version',
name: 'Anchor',
regex: /anchor-cli (\d+\.\d+\.\d+)/,
},
avm: {
command: 'avm --version',
name: 'AVM ',
regex: /avm (\d+\.\d+\.\d+)/,
},
rust: {
command: 'rustc --version',
name: 'Rust ',
regex: /rustc (\d+\.\d+\.\d+)/,
},
solana: {
command: 'solana --version',
name: 'Solana',
regex: /solana-cli (\d+\.\d+\.\d+)/,
},
}

export type VersionCommand = keyof typeof commandMap

export function getVersionCommand(command: VersionCommand): { command: string; name: string; regex: RegExp } {
return commandMap[command]
}

export function getVersionCommandNames(): string[] {
return Object.keys(commandMap)
}
31 changes: 31 additions & 0 deletions src/utils/get-version-urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { VersionCommand } from './get-version-command'

type VersionUrls = {
install?: string
update?: string
}

const urls: Record<VersionCommand, VersionUrls> = {
adb: {
install:
'https://docs.expo.dev/get-started/set-up-your-environment/?platform=android&device=physical&mode=development-build&buildEnv=local',
},
anchor: {
install: 'https://www.anchor-lang.com/docs/installation',
update: 'https://www.anchor-lang.com/release-notes/{required}',
},
solana: {
install: 'https://docs.solana.com/cli/install-solana-cli-tools',
},
}

export function getVersionUrls(command: VersionCommand, required: string): VersionUrls {
const { install, update } = urls[command] ?? {}

return install || update
? {
install: install?.replace('{required}', required),
update: (update ?? install)?.replace('{required}', required),
}
: {}
}
33 changes: 3 additions & 30 deletions src/utils/get-version.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,8 @@
import { getVersionCommand, VersionCommand } from './get-version-command'
import { parseVersion } from './parse-version'

export const versionCommands: Record<string, { command: string; name: string; regex: RegExp }> = {
adb: {
command: 'adb --version',
name: 'Adb ',
regex: /Version (\d+\.\d+\.\d+)/,
},
anchor: {
command: 'anchor --version',
name: 'Anchor',
regex: /anchor-cli (\d+\.\d+\.\d+)/,
},
avm: {
command: 'avm --version',
name: 'AVM ',
regex: /avm (\d+\.\d+\.\d+)/,
},
rust: {
command: 'rustc --version',
name: 'Rust ',
regex: /rustc (\d+\.\d+\.\d+)/,
},
solana: {
command: 'solana --version',
name: 'Solana',
regex: /solana-cli (\d+\.\d+\.\d+)/,
},
}

export function getVersion(command: keyof typeof versionCommands): string | undefined {
const cmd = versionCommands[command]
export function getVersion(command: VersionCommand): string | undefined {
const cmd = getVersionCommand(command)
if (!cmd) {
throw new Error(`Unknown command ${command}`)
}
Expand Down
15 changes: 15 additions & 0 deletions src/utils/init-script-delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { log } from '@clack/prompts'
import { writeFileSync } from 'node:fs'
import { GetArgsResult } from './get-args-result'
import { getPackageJson } from './get-package-json'
import { initScriptKey } from './init-script-schema'

export function initScriptDelete(args: GetArgsResult) {
const tag = `initScriptDelete`
const { path, contents } = getPackageJson(args.targetDirectory)
delete contents[initScriptKey]
writeFileSync(path, JSON.stringify(contents, undefined, 2) + '\n')
if (args.verbose) {
log.warn(`${tag}: deleted ${initScriptKey} from package.json`)
}
}
16 changes: 16 additions & 0 deletions src/utils/init-script-instructions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { log } from '@clack/prompts'
import { InitScriptInstructions } from './init-script-schema'

export function initScriptInstructions(instructions?: InitScriptInstructions, verbose = false): string[] {
const tag = `initScriptInstructions`
if (!instructions || instructions.length === 0) {
if (verbose) {
log.warn(`${tag}: no instructions found`)
}
return []
}
if (verbose) {
log.warn(`${tag}: ${instructions.length} instructions found`)
}
return instructions
}
54 changes: 54 additions & 0 deletions src/utils/init-script-rename.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { log } from '@clack/prompts'
import { join } from 'node:path'
import { ensureTargetPath } from './ensure-target-path'
import { GetArgsResult } from './get-args-result'
import { getPackageJson } from './get-package-json'
import { InitScriptRename } from './init-script-schema'
import { searchAndReplace } from './search-and-replace'
import { namesValues } from './vendor/names'

export async function initScriptRename(args: GetArgsResult, rename?: InitScriptRename, verbose = false) {
const tag = `initScriptRename`
const { contents } = getPackageJson(args.targetDirectory)
// Rename template from package.json to project name throughout the whole project
if (contents.name) {
if (args.verbose) {
log.warn(`${tag}: renaming template name '${contents.name}' to project name '${args.name}'`)
}
await searchAndReplace(args.targetDirectory, [contents.name], [args.name], false, verbose)
}

// Return early if there are no renames defined in the init script
if (!rename) {
if (args.verbose) {
log.warn(`${tag}: no renames found`)
}
return
}

// Loop through each word in the rename object
for (const from of Object.keys(rename)) {
// Get the 'to' property from the rename object
const to = rename[from].to.replace('{{name}}', args.name.replace(/-/g, ''))

// Get the name matrix for the 'from' and the 'to' value
const fromNames = namesValues(from)
const toNames = namesValues(to)

for (const path of rename[from].paths) {
const targetPath = join(args.targetDirectory, path)
if (!(await ensureTargetPath(targetPath))) {
log.error(`${tag}: target does not exist ${targetPath}`)
continue
}
if (args.verbose) {
log.warn(`${tag}: ${targetPath} -> ${fromNames.join('|')} -> ${toNames.join('|')}`)
}
await searchAndReplace(targetPath, fromNames, toNames, args.dryRun, args.verbose)
}
}

if (args.verbose) {
log.warn(`${tag}: done`)
}
}
Loading