Skip to content

Commit

Permalink
feat: add command "eject" to override default files in the current pr…
Browse files Browse the repository at this point in the history
…oject
  • Loading branch information
nknapp committed Jul 2, 2020
1 parent ddd8d7a commit 8040572
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 0 deletions.
9 changes: 9 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ module.exports = function(argv, console, done) {
.then(() => quit(null, 'OK'), quit)
})

program
.command('eject [prefix] [filename]')
.description("Extract part of thought's default templates into the local configuration directory")
.action(function(prefix, filename) {
changeDir()
.then(() => require('../lib/eject.js')(prefix, filename))
.then(() => quit(null, 'OK'), quit)
})

program
.command('up-to-date')
.description(
Expand Down
82 changes: 82 additions & 0 deletions lib/eject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* eslint-disable no-console */

const customize = require('customize')
const debug = require('debug')('though:eject')
const fs = require('fs-extra')
const path = require('path')

module.exports = async function eject(optionalPrefix, filename) {
if (filename == null) {
return logEjectableFiles(optionalPrefix)
}

const config = await customize()
// Load `customize`-spec
.load(require('../customize')('.'))
.buildConfig()

switch (optionalPrefix) {
case 'template':
return ejectFile({
source: config.handlebars.templates[filename],
targetFile: path.join('.thought', 'templates', filename),
notFoundMessage: `There is no template "${filename}"!`
})
case 'partial':
return ejectFile({
source: config.handlebars.partials[filename],
targetFile: path.join('.thought', 'partials', filename),
notFoundMessage: `There is no partial "${filename}"!`
})
}
}

async function logEjectableFiles(optionalPrefix) {
const config = await customize()
// Load `customize`-spec
.load(require('../customize')('.'))
.buildConfig()

debug(config)

console.log('I can eject the following files for you: ')
switch (optionalPrefix) {
case undefined:
case null:
logDefaultFiles(config.handlebars.templates, { prefix: 'template' })
logDefaultFiles(config.handlebars.partials, { prefix: 'partial' })
break
case 'template':
logDefaultFiles(config.handlebars.templates, { prefix: 'template' })
break
case 'partial':
logDefaultFiles(config.handlebars.partials, { prefix: 'partial' })
break
default:
throw new Error(`Unknown prefix "${optionalPrefix}", try without prefix!"`)
}
}

function logDefaultFiles(fileObject, { prefix }) {
Object.entries(fileObject).forEach(([fileName, file]) => {
if (!isOverridden(file)) {
console.log(` ${prefix} ${fileName}`)
}
})
}

async function ejectFile({ source, targetFile, notFoundMessage }) {
if (source == null) {
throw new Error(notFoundMessage)
}
if (isOverridden(source)) {
throw new Error(`File "${source.path}" already exists in this project!`)
}

console.log(`Ejecting "${targetFile}"`)
await fs.copy(source.path, targetFile)
}

function isOverridden(file) {
return file.path.match(/^\.thought/)
}
107 changes: 107 additions & 0 deletions test/cli-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const expect = chai.expect
const exec = require('../lib/utils/exeq')
const cp = require('child_process')

const fs = require('fs-extra')
const path = require('path')

describe('The cli script', function() {
this.timeout(30000)
const scenario = new Scenario('simple-project')
Expand Down Expand Up @@ -84,6 +87,110 @@ describe('The cli script', function() {
})
)
})

describe('the "eject" command', () => {
const ejectScenario = new Scenario('with-partial-and-template')

it('should show a list of ejectable files if no parameter is provided', async function() {
await ejectScenario.prepareAndRun(() =>
runMockThought('eject').then(result => {
expect(result.code).to.equal(0)
expect(result.stdout).to.match(/partial howitworks\.md\.hbs/)
expect(result.stdout).to.match(/template README\.md\.hbs/)
expect(result.stdout).not.to.match(/usage\.md\.hbs/)

expect(result.stderr).to.equal('')
})
)
})

it('should show only ejectable templates, if the prefix is "template"', async function() {
await ejectScenario.prepareAndRun(() =>
runMockThought('eject', 'template').then(result => {
expect(result.code).to.equal(0)
expect(result.stdout).not.to.match(/partial howitworks\.md\.hbs/)
expect(result.stdout).to.match(/template README\.md\.hbs/)
expect(result.stderr).to.equal('')
})
)
})

it('should show only ejectable partials, if the prefix is "partials"', async function() {
await ejectScenario.prepareAndRun(() =>
runMockThought('eject', 'partial').then(result => {
expect(result.code).to.equal(0)
expect(result.stdout).to.match(/partial howitworks\.md\.hbs/)
expect(result.stdout).not.to.match(/template README\.md\.hbs/)
expect(result.stderr).to.equal('')
})
)
})

it('should eject a partial if a filename is specified and the prefix is "partial"', async function() {
await ejectScenario.prepareAndRun(async () => {
const result = await runMockThought('eject', 'partial', 'howitworks.md.hbs')

expect(result.code).to.equal(0)
expect(result.stdout).to.match(/Ejecting "\.thought.partials.howitworks\.md\.hbs"/)

const ejectedPath = path.join('.thought', 'partials', 'howitworks.md.hbs')
const ejectedFileContents = await ejectScenario.readActual(ejectedPath)

const defaultPath = require.resolve('../handlebars/partials/howitworks.md.hbs')
const defaultFileContents = await fs.readFile(defaultPath, 'utf-8')

expect(ejectedFileContents).to.equal(defaultFileContents)
})
})

it('should eject a template if a filename is specified and the prefix is "template"', async function() {
await ejectScenario.prepareAndRun(async () => {
const result = await runMockThought('eject', 'template', 'README.md.hbs')

expect(result.code).to.equal(0)
expect(result.stdout).to.match(/Ejecting "\.thought.templates.README\.md\.hbs"/)
expect(result.stderr).to.equal('')

const ejectedPath = path.join('.thought', 'templates', 'README.md.hbs')
const ejectedFileContents = await ejectScenario.readActual(ejectedPath)

const defaultPath = require.resolve('../handlebars/templates/README.md.hbs')
const defaultFileContents = await fs.readFile(defaultPath, 'utf-8')

expect(ejectedFileContents).to.equal(defaultFileContents)
})
})

it('should give sensible error messages', async function() {
await ejectScenario.prepareAndRun(async () => {
await expectError({ args: ['eject', 'temp'], errorMessage: 'Unknown prefix "temp", try without prefix!' })

await expectError({
args: ['eject', 'template', 'RED.md.hbs'],
errorMessage: 'There is no template "RED.md.hbs"!'
})
await expectError({
args: ['eject', 'template', 'anotherFile.md.hbs'],
errorMessage: `File ".thought/templates/anotherFile.md.hbs" already exists in this project!`
})

await expectError({
args: ['eject', 'partial', 'RED.md.hbs'],
errorMessage: 'There is no partial "RED.md.hbs"!'
})
await expectError({
args: ['eject', 'partial', 'usage.md.hbs'],
errorMessage: `File ".thought/partials/usage.md.hbs" already exists in this project!`
})
})
})

async function expectError({ args, errorMessage }) {
const result = await runMockThought(...args)
expect(result.stderr).to.contain(errorMessage)
expect(result.code).to.equal(1)
}
})
})

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Overridden usage file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is another file
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# simple-project

[![NPM version](https://img.shields.io/npm/v/simple-project.svg)](https://npmjs.com/package/simple-project)

> A simple description

# Installation

```
npm install simple-project
```

Overridden usage file


# License

`simple-project` is published under the ISC-license.

No file "LICENSE*" found



# Contributing guidelines

See [CONTRIBUTING.md](CONTRIBUTING.md).
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Beschreibung
* @param {string=} param
*/
function abc(param) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "eject-project",
"version": "1.0.0",
"description": "A simple description",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/unit-test/eject-project.git"
},
"author": "",
"license": "ISC",
"devDependencies": {
"fs-walker": "^1.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Overridden usage file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is another file
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Beschreibung
* @param {string=} param
*/
function abc(param) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "eject-project",
"version": "1.0.0",
"description": "A simple description",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/unit-test/eject-project.git"
},
"author": "",
"license": "ISC",
"devDependencies": {
"fs-walker": "^1.0.0"
}
}

0 comments on commit 8040572

Please sign in to comment.