Skip to content

Commit

Permalink
feat: use basepath name for project name and add on-create hook (#4)
Browse files Browse the repository at this point in the history
* fix: add target directory name question

* feat: add on create hook for dynamic section

* refactor: move to hooks folder

* feat: add cloudflare-workers' on-create hook

* feat: add log when path is unspecified

* feat: use basepath for project name

* refactor(on-create): rename REPLACE_PROJECT_NAME_KEY to PROJECT_NAME_REPLACE_KEY

* refactor: move Hook type to types.ts

* refactor: rename on-create to after-create

* refactor: rename on-create.ts to after-create.ts

* refactor: make more readable for variable

* fix: rename replacing key to `%%PROJECT_NAME%%`

* refactor: rename forgotten after-create-hook

* chore: add vitest

* fix: prepare for testing about after-create.ts

* test: add after-create test case

* make it as a class

* fix: add return type to applied hook results

* fix: expand and pass arguments

* test: add applyHook test for Hook class

* test: fix test for new hook style

* fix: use replaceAll for multiple target

* test: update test case for multiple replacing

---------

Co-authored-by: Yusuke Wada <[email protected]>
  • Loading branch information
sor4chi and yusukebe authored Oct 15, 2023
1 parent efa2798 commit 9c4f1b3
Show file tree
Hide file tree
Showing 7 changed files with 642 additions and 1 deletion.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.2.6",
"scripts": {
"build": "tsx ./build.ts",
"test": "vitest --run",
"prepack": "yarn build",
"release": "np"
},
Expand Down Expand Up @@ -32,6 +33,7 @@
"np": "^7.6.3",
"prompts": "^2.4.2",
"tsx": "^3.12.2",
"vitest": "^0.34.6",
"yargs-parser": "^21.1.1"
}
}
15 changes: 15 additions & 0 deletions src/hook.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { describe, expect, it, vi } from 'vitest'
import { Hook } from './hook'

describe('Hook', () => {
it('`Hook.applyHook()` runs all hooks for a template', () => {
const hook = new Hook()
const fn1 = vi.fn()
const fn2 = vi.fn()
hook.addHook('test-template', fn1)
hook.addHook('test-template', fn2)
hook.applyHook('test-template', {})
expect(fn1).toHaveBeenCalled()
expect(fn2).toHaveBeenCalled()
})
})
26 changes: 26 additions & 0 deletions src/hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export class Hook<HookFunction extends (...args: any[]) => any> {
#hookMap: Map<string, HookFunction[]>
constructor() {
this.#hookMap = new Map<string, HookFunction[]>()
}

addHook(templateName: string, hook: HookFunction) {
const hooks = this.#hookMap.get(templateName) || []
hooks.push(hook)
this.#hookMap.set(templateName, hooks)
}

applyHook(
templateName: string,
...hookOptions: Parameters<HookFunction>
): ReturnType<HookFunction>[] {
const hooks = this.#hookMap.get(templateName)
const results: ReturnType<HookFunction>[] = []
if (hooks) {
hooks.forEach((hook) => {
results.push(hook(...hookOptions))
})
}
return results
}
}
42 changes: 42 additions & 0 deletions src/hooks/after-create.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { join } from 'path'
import { describe, expect, it, vi } from 'vitest'
import { afterCreateHook } from './after-create'

describe('afterCreateHook', () => {
describe('cloudflare-workers', () => {
describe('rewriteWranglerHook', () => {
it('rewrites the wrangler.toml file with the project name', async () => {
vi.mock('fs', () => {
const wrangler = `
name = "%%PROJECT_NAME%%"
[env.staging]
name = "%%PROJECT_NAME%%-staging"
`.trim()

return {
readFileSync: vi.fn().mockReturnValue(wrangler),
writeFileSync: vi.fn(),
}
})
const { readFileSync, writeFileSync } = await import('fs')

const projectName = 'test-project'
const directoryPath = './tmp'
const wranglerPath = join(directoryPath, 'wrangler.toml')
const replaced = `
name = "${projectName}"
[env.staging]
name = "${projectName}-staging"
`.trim()
afterCreateHook.applyHook('cloudflare-workers', {
projectName,
directoryPath,
})
expect(readFileSync).toHaveBeenCalledWith(wranglerPath, 'utf-8')
expect(writeFileSync).toHaveBeenCalledWith(wranglerPath, replaced)
})
})
})
})
24 changes: 24 additions & 0 deletions src/hooks/after-create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { readFileSync, writeFileSync } from 'fs'
import * as path from 'path'
import { Hook } from '../hook'

type AfterHookOptions = {
projectName: string
directoryPath: string
}

type AfterHookFunction = (options: AfterHookOptions) => void

const afterCreateHook = new Hook<AfterHookFunction>()

afterCreateHook.addHook(
'cloudflare-workers',
({ projectName, directoryPath }) => {
const wranglerPath = path.join(directoryPath, 'wrangler.toml')
const wrangler = readFileSync(wranglerPath, 'utf-8')
const rewritten = wrangler.replaceAll('%%PROJECT_NAME%%', projectName)
writeFileSync(wranglerPath, rewritten)
}
)

export { afterCreateHook }
32 changes: 31 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import prompts from 'prompts'
import yargsParser from 'yargs-parser'
import { version } from '../package.json'
import { viaContentsApi } from './github.js'
import { afterCreateHook } from './hooks/after-create'

const directoryName = 'templates'
const config = {
Expand All @@ -29,7 +30,6 @@ async function main() {

let args = yargsParser(process.argv.slice(2))

const target = (args._[0] && String(args._[0])) || '.'
const templateArg = args.template

const templateDirs = await viaContentsApi(config)
Expand All @@ -45,6 +45,27 @@ async function main() {
})
let templateNames = [...Object.values(templates)] as { name: string }[]

let target = ''
let projectName = ''
if (args._[0]) {
target = args._[0].toString()
projectName = target
console.log(`${bold(`${green(`✔`)} Using target directory`)}${target}`)
} else {
const answer = await prompts({
type: 'text',
name: 'target',
message: 'Target directory',
initial: 'my-app',
})
target = answer.target
if (answer.target === '.') {
projectName = path.basename(process.cwd())
} else {
projectName = path.basename(answer.target)
}
}

const templateName =
templateArg ||
(
Expand Down Expand Up @@ -102,6 +123,15 @@ async function main() {
})
})

try {
afterCreateHook.applyHook(templateName, {
projectName,
directoryPath: path.join(process.cwd(), target),
})
} catch (e) {
throw new Error(`Error running hook for ${templateName}: ${e.message}`)
}

console.log(bold(green('✔ Copied project files')))
}

Expand Down
Loading

0 comments on commit 9c4f1b3

Please sign in to comment.