counsel
Version:
the end of boilerplate. automatically bake structure, opinions, and business rules into projects
57 lines (52 loc) • 1.85 kB
text/typescript
import { promisify } from 'util'
import { Rule, ContextWithRule } from '../counsel' // eslint-disable-line no-unused-vars
import * as path from 'path'
import glob from 'glob'
const DEFAULT_IGNORE = ['**/README.*', '**/package.json', '**/node_modules/**']
export interface FilenameFormatRule extends Rule {
filenameFormatExtensions?: string[] // js,coffee,etc
filenameFormatExclude?: string[] // filename, path. default ignores README*, package.json
filenameFormatFunction: (filename: string) => string // accepts file basename as input
}
const removeValid = (filenames: string[], fn: any) => {
if (!filenames.length) return filenames
return filenames.filter(f => {
const extname = path.extname(f)
const basename = path.basename(f).replace(extname, '')
return basename !== fn(basename)
})
}
export const check = (opts: ContextWithRule<FilenameFormatRule>) => {
const {
ctx: { projectDirname: targetProjectDirname },
rule: {
filenameFormatExclude: exclude = [],
filenameFormatExtensions: extensions = [],
filenameFormatFunction: formatFunction
}
} = opts
const cleanedExtensions = extensions
.filter(Boolean)
.map(f => f.trim())
.map(f => f.replace(/^\./, ''))
.map(f => `**/*.${f}`)
const cleanedExclude = exclude
.concat(DEFAULT_IGNORE)
.filter(Boolean)
.map(f => ((f as string).trim ? (f as string).trim() : f))
return promisify(glob)(cleanedExtensions.join('|'), {
cwd: targetProjectDirname,
ignore: cleanedExclude
})
.then(filenames => removeValid(filenames, formatFunction))
.then(violating => {
if (violating.length) {
throw new Error(
[
'the following files are in violation of a file format naming rule:',
`\t${violating.join(', ')}`
].join('\n')
)
}
})
}