src/collection.js
import Document from './document'
import DocumentImporter from './importer'
import { propertyHider } from './util'
import snakeCase from 'lodash/snakeCase'
import camelCase from 'lodash/camelCase'
import isAbsolutePath from 'path-is-absolute'
import { resolve } from 'path'
import flatten from 'lodash/flatten'
import pathToRegexp from 'path-to-regexp'
const PACKAGE = 'PACKAGE'
const SCRIPT = 'SCRIPT'
const DOCUMENT = 'DOCUMENT'
const SETTINGS_FILE = 'SETTINGS_FILE'
const COPY_FILE = 'COPY_FILE'
const PROJECT = 'PROJECT'
const VECTOR = 'VECTOR'
export const PatternMapping = {
'Package': ['**/package.json'],
'Document': ['**/*.md'],
'Script': ['**/*.js'],
'SettingsFile': ['**/*.yml'],
'CopyFile': ['**/*.yml'],
'Vector': ['**/*.svg'],
'Project': ['**/package.json', '**/*.js', '**/*.md', '**/*.yml']
}
export function create(options = {}) {
return Collection.create(options)
}
export class Collection {
static mount (base = process.cwd(), options = {}) {
if (base.startsWith('.')) {
base = base.replace(/^\./, process.cwd())
}
if (!isAbsolutePath(base)) {
base = resolve(process.cwd(), base)
}
return create({
cwd: base,
base,
...options,
}).imported.cached
}
static create (options = {}) {
return new Collection(options)
}
constructor (options = {}) {
const hide = propertyHider(this, true)
hide('hide', hide)
hide('base', options.base || process.cwd())
hide('files', [])
hide('options', { ignore: [], ...options })
this.importer = DocumentImporter.create(
this.base,
this.patterns,
)
const cache = {}
hide('cache', cache)
const scopes = {}
hide('_scopes', scopes)
}
get scopes() {
const collection = this
const base = {
available: Object.keys(collection._scopes),
}
base.available.forEach(
scopeName => Object.defineProperty(base, scopeName, {
configurable: false,
false: true,
get: function() {
const cfg = collection.scope(scopeName)
return collection.files.filter(file => file.path.match(cfg.route))
}
})
)
return base
}
get patterns() {
return this.typePatterns.concat(this.ignorePatterns)
}
get ignorePatterns () {
return this.options.ignore.map(p => p.startsWith('!') ? p : `!${p}`).concat('!node_modules/**')
}
get imported() {
if(!this.filesLoaded) {
this.loadFiles(this.importer.readFilesSync(false))
}
return this.cached
}
loadFiles (files) {
this.files.length = 0
this.files.push(
...files.map(file => {
return file
})
)
this.filesLoaded = true
return this.files
}
runImporter(cacheDocuments = true) {
return this.importer.readFiles(false)
.then(files => this.files.push(...files))
.then(() => cacheDocuments ? this.cached : this)
.catch(error => error)
}
get scope() {
let fn = this.defineScope.bind(this)
return Object.assign(fn, this.scopes)
return fn
}
defineScope (scopeName, routePattern, options) {
const name = camelCase(scopeName)
if (this._scopes[name] && !routePattern && !options) {
return this._scopes[name]
}
return this._scopes[name] = {
route: pathToRegexp(routePattern),
key: name,
name: scopeName,
...(options || {}),
}
}
get ready() {
return this.importer.status === 'LOADED'
? Promise.resolve(this)
: this.runImporter().then(importer => importer)
}
get documentOptions() {
return {
base: this.base,
collectionType: this.options.type,
}
}
get cached() {
this.files.forEach(file => {
this.cache[file.path] = this.cache[file.path]
|| new Document(file.path, this.documentOptions)
})
return this
}
get documents() {
return Object.values(this.cache)
}
get type() {
return snakeCase(this.options.type || 'Project').replace(/s$/i, '')
}
get typePatterns () {
switch (this.type.toUpperCase()) {
case PACKAGE:
return PatternMapping.Package
break
case SCRIPT:
return PatternMapping.Script
break
case SETTINGS_FILE, COPY_FILE:
return PatternMapping.SettingsFile
break
case DOCUMENT:
return PatternMapping.Document
break
case VECTOR:
return PatternMapping.Vector
break
case PROJECT:
default:
return PatternMapping.Project
}
}
}
export default Collection