Skip to content

Commit

Permalink
feat: allow change context value (name or path) on the fly
Browse files Browse the repository at this point in the history
  • Loading branch information
ambar committed Oct 11, 2021
1 parent e5ebfdf commit dabec0f
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 37 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 116,8 @@ npm init <your-initializer> <your-project>
- `gitInit(message: string) => Promise` init git repository
- `prompts(Array | Object) => Promise` see [prompts](https://github.com/terkelg/prompts#-usage)
- `context` generator context
- `path: string` new project's path
- `name: string` new project's name
- `path: string` new project's path (it's also a setter)
- `name: string` new project's name (it's also a setter)
- `argv: Object` command line arguments, parsed by [mri](https://www.npmjs.com/package/mri)

## Testing
Expand Down
62 changes: 38 additions & 24 deletions gogen/lib/create.ts
Original file line number Diff line number Diff line change
@@ -1,3 1,4 @@
import assert from 'assert'
import {promises as fsp} from 'fs'
import path from 'path'
import {promisify} from 'util'
Expand Down Expand Up @@ -27,9 28,6 @@ type ReturnPair = Awaited<ReturnType<typeof loadGenerator>>
export type API = ReturnPair[1]
export type Context = Omit<ReturnPair[2], 'install' | 'gitInit' | 'prompts'>

const partialOptions = (fn: any, partial: any) => (arg: any, options: any) =>
fn(arg, {...options, ...partial})

const getPathType = (path: any) => {
if (/^[~./]/.test(path)) {
return 'local'
Expand All @@ -40,6 38,9 @@ const getPathType = (path: any) => {
return 'npm'
}

const stringRequired = (v: unknown) =>
assert.ok(typeof v === 'string' && v !== '')

let defaultIgnore = ['**/node_modules/**', '**/.git', '**/.DS_Store']
async function* globFiles(
globs: string[],
Expand Down Expand Up @@ -87,36 88,56 @@ export const loadGenerator = async (argv: ParsedArgv, {mock}: any = {}) => {
}
}

// dest path
const destPath = path.resolve(directory)
const name = path.basename(destPath)

// TODO: change cwd to dest path?
// if (!fs.existsSync(destPath)) { fs.mkdirSync(destPath) }
// process.chdir(destPath)
let _path = path.resolve(directory)
const context = {
get path() {
return _path
},
set path(v) {
stringRequired(v)
_path = path.resolve(v)
},
get name() {
return path.basename(context.path)
},
set name(v) {
stringRequired(v)
context.path = path.format({...path.parse(context.path), base: v})
},
argv,
}

// utils, non-stream API
let extra = {
install: partialOptions(install, {cwd: destPath}) as typeof install,
gitInit: partialOptions(gitInit, {cwd: destPath}) as typeof gitInit,
install: ((arg, opts) =>
install(arg, {cwd: context.path, ...opts})) as typeof install,
gitInit: ((arg, opts) =>
gitInit(arg, {cwd: context.path, ...opts})) as typeof gitInit,
prompts,
}

const api = {
src: (globs: string[]) =>
stream.Readable.from(globFiles(globs, {cwd: srcPath}))
.pipe(dotgitignore())
.pipe(packages({name})),
dest: (folder = destPath) =>
through2.obj(async (file: VFile, enc: any, next: any) => {
let outBase = path.resolve(srcPath, folder)
.pipe(packages({name: context.name})),
dest: (folder: string) => {
folder = folder ?? context.path
if (folder !== context.path) {
context.path = folder
}
console.info(`Creating ${colors.green(context.name)}...`)
return through2.obj(async (file: VFile, _enc, next) => {
let outBase = path.resolve(srcPath, context.path)
let outPath = path.resolve(outBase, file.relative)
file.base = outBase
file.path = outPath
// TODO: add log
await fsp.mkdir(file.dirname, {recursive: true})
await fsp.writeFile(file.path, file.contents as Buffer)
next(null, file)
}),
})
},
// Node v15 has native support (`import {pipeline} from 'stream/promises'`)
pipeline: promisify(stream.pipeline),
packages,
Expand All @@ -125,19 146,12 @@ export const loadGenerator = async (argv: ParsedArgv, {mock}: any = {}) => {
...extra,
}

const context = {
path: destPath,
name,
argv,
}

if (Array.isArray(mock) && mock.length) {
const [mockAPI, mockContext] = mock
Object.assign(api, mockAPI)
Object.assign(context, mockContext)
}

console.info(`Creating ${colors.green(name)}...`)
const rcFile = path.resolve(srcPath, '.gogenrc.js')
const fn = (await boolify(fsp.stat(rcFile))) ? require(rcFile) : defaultRc
return [fn, api, context] as const
Expand Down
6 changes: 3 additions & 3 deletions gogen/lib/utils/boolify.ts
Original file line number Diff line number Diff line change
@@ -1,7 1,7 @@
const boolify = (promise: Promise<any>) =>
const boolify = (promise: Promise<unknown>) =>
promise.then(
(_: any) => true,
(_: any) => false
() => true,
() => false
)

export default boolify
33 changes: 26 additions & 7 deletions gogen/test/cli.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 4,17 @@ import glob from 'glob'
import run from '../lib/run'
import createTempDir from '../lib/utils/createTempDir'

jest.setTimeout(10 * 1000)

const globDir = (dir) =>
glob
.sync('**', {
cwd: dir,
dot: true,
ignore: ['.git/*/**', '.yarn/**'],
})
.sort()

test('run error', async () => {
jest.spyOn(console, 'error').mockImplementation(() => {})
jest.spyOn(process, 'exit').mockImplementation((code) => {
Expand All @@ -23,13 34,7 @@ test('run ok from local', async () => {
description: 'superb',
devDependencies: {olt: expect.anything()},
})
const files = glob
.sync('**', {
cwd: dist,
dot: true,
ignore: ['.git/*/**', '.yarn/**'],
})
.sort()
const files = globDir(dist)
expect(files).toMatchInlineSnapshot(`
Array [
".git",
Expand All @@ -44,3 49,17 @@ Array [
]
`)
})

test('change dest', async () => {
const dist = createTempDir({prefix: 'gogen'})
const generator = path.resolve(__dirname, 'fixtures/change-dest')
await run([generator, dist])
const files = globDir(dist)
expect(files).toMatchInlineSnapshot(`
Array [
"subfolder",
"subfolder/README.md.t",
"subfolder/package.json",
]
`)
})
13 changes: 13 additions & 0 deletions gogen/test/fixtures/change-context/.gogenrc.js
Original file line number Diff line number Diff line change
@@ -0,0 1,13 @@
module.exports = async (
{src, dest, pipeline, template, install, gitInit},
context
) => {
context.name = `prefix-${context.name}`
await pipeline(
src('template/**'),
template({name: context.name, description: 'My [gogen](#) project'}),
dest()
)
await install(['olt'], {dev: true, silent: true})
await gitInit()
}
6 changes: 6 additions & 0 deletions gogen/test/fixtures/change-context/package.json
Original file line number Diff line number Diff line change
@@ -0,0 1,6 @@
{
"name": "test-basic",
"private": true,
"version": "0.0.0",
"dependencies": {}
}
9 changes: 9 additions & 0 deletions gogen/test/fixtures/change-context/template/README.md.t
Original file line number Diff line number Diff line change
@@ -0,0 1,9 @@
# <%= name %>

<%= description %>

## Usage

```bash
npm install <%= name %>
```
6 changes: 6 additions & 0 deletions gogen/test/fixtures/change-context/template/package.json
Original file line number Diff line number Diff line change
@@ -0,0 1,6 @@
{
"name": "${name}",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
5 changes: 5 additions & 0 deletions gogen/test/fixtures/change-dest/.gogenrc.js
Original file line number Diff line number Diff line change
@@ -0,0 1,5 @@
const {join} = require('path')

module.exports = async ({src, dest, pipeline}, ctx) => {
await pipeline(src('template/**'), dest(join(ctx.path, 'subfolder')))
}
6 changes: 6 additions & 0 deletions gogen/test/fixtures/change-dest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 1,6 @@
{
"name": "test-basic",
"private": true,
"version": "0.0.0",
"dependencies": {}
}
9 changes: 9 additions & 0 deletions gogen/test/fixtures/change-dest/template/README.md.t
Original file line number Diff line number Diff line change
@@ -0,0 1,9 @@
# <%= name %>

<%= description %>

## Usage

```bash
npm install <%= name %>
```
6 changes: 6 additions & 0 deletions gogen/test/fixtures/change-dest/template/package.json
Original file line number Diff line number Diff line change
@@ -0,0 1,6 @@
{
"name": "${name}",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
18 changes: 17 additions & 1 deletion gogen/test/mock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 20,21 @@ Array [
"package.json",
]
`)
expect(readFile('package.json')).toMatch(/mylib/)
expect(JSON.parse(readFile('package.json'))).toMatchObject({
name: 'mylib',
})
})

test('change context', async () => {
const generator = path.resolve(__dirname, 'fixtures/change-context')
const {files, readFile} = await mock(generator, 'mylib')
expect(files).toMatchInlineSnapshot(`
Array [
"README.md",
"package.json",
]
`)
expect(JSON.parse(readFile('package.json'))).toMatchObject({
name: 'prefix-mylib',
})
})

0 comments on commit dabec0f

Please sign in to comment.