2018-12-07 00:03:41 +03:00
|
|
|
const chokidar = require('chokidar')
|
2018-12-01 03:17:36 +03:00
|
|
|
const klaw = require('klaw-sync')
|
|
|
|
const minimatch = require('minimatch')
|
2018-12-07 01:00:24 +03:00
|
|
|
const matter = require('gray-matter')
|
2018-12-01 03:17:36 +03:00
|
|
|
const {green, red, yellow} = require('colorette')
|
|
|
|
const {basename, dirname, join} = require('path')
|
2018-12-07 01:00:24 +03:00
|
|
|
const {copySync, ensureDirSync, existsSync, readFileSync, removeSync} = require('fs-extra')
|
2018-12-07 00:03:41 +03:00
|
|
|
const {getIgnored, setIgnored} = require('./ignore')
|
2018-12-01 03:17:36 +03:00
|
|
|
|
2018-12-07 00:23:43 +03:00
|
|
|
const sourceDir = join(__dirname, '../../modules')
|
|
|
|
const destDir = join(__dirname, '../pages/css')
|
|
|
|
const ignoreFile = join(destDir, '.gitignore')
|
|
|
|
|
|
|
|
const map = {
|
|
|
|
'../CHANGELOG.md': 'whats-new/changelog.md',
|
|
|
|
'primer/README.md': false, // 'packages/primer.md',
|
|
|
|
'primer-base/README.md': false, // 'support/base.md',
|
|
|
|
'primer-core/README.md': false, // 'packages/primer-core.md',
|
|
|
|
'primer-layout/README.md': 'objects/layout.md',
|
|
|
|
'primer-layout/docs/*.md': path => `objects/${basename(path)}`,
|
|
|
|
'primer-marketing-support/README.md': 'support/marketing-variables.md',
|
2018-12-07 01:00:24 +03:00
|
|
|
'primer-marketing-type/docs/index.md': 'utilities/marketing-type.md',
|
2018-12-07 00:23:43 +03:00
|
|
|
'primer-marketing-utilities/README.md': false, // 'utilities/marketing.md',
|
|
|
|
'primer-marketing-utilities/docs/*.md': path => `utilities/marketing-${basename(path)}`,
|
|
|
|
'primer-marketing/README.md': false, // 'packages/primer-marketing.md',
|
|
|
|
'primer-product/README.md': false, // 'packages/primer-product.md',
|
|
|
|
'primer-support/README.md': false, // 'support/index.md',
|
|
|
|
'primer-support/docs/*.md': path => `support/${basename(path)}`,
|
|
|
|
'primer-table-object/README.md': 'objects/table-object.md',
|
|
|
|
'primer-utilities/README.md': false, // 'utilities/index.md',
|
|
|
|
'primer-utilities/docs/*.md': path => `utilities/${basename(path)}`,
|
|
|
|
// this is a catch-all rule that needs to go last so that it doesn't override others
|
|
|
|
'primer-*/README.md': path => `components/${shortName(path)}.md`,
|
|
|
|
}
|
|
|
|
|
2018-12-07 00:03:41 +03:00
|
|
|
module.exports = {sync, watch}
|
2018-12-01 03:17:36 +03:00
|
|
|
|
2018-12-07 00:23:43 +03:00
|
|
|
function sync({debug = false}) {
|
2018-12-07 00:03:41 +03:00
|
|
|
const log = debug ? console.warn : noop
|
|
|
|
const ignored = getIgnored(ignoreFile)
|
|
|
|
for (const file of ignored) {
|
2018-12-07 01:00:24 +03:00
|
|
|
try {
|
|
|
|
removeSync(file)
|
|
|
|
log(`${yellow('x')} removed: ${file}`)
|
|
|
|
} catch (error) {
|
|
|
|
log(`${red('x')} missing: ${file}`)
|
|
|
|
}
|
2018-12-07 00:03:41 +03:00
|
|
|
}
|
2018-12-07 01:00:24 +03:00
|
|
|
console.time('get links')
|
2018-12-07 00:03:41 +03:00
|
|
|
const links = getLinks(sourceDir, destDir, map)
|
2018-12-07 01:00:24 +03:00
|
|
|
console.timeEnd('get links')
|
|
|
|
if (links.length) {
|
|
|
|
log(yellow(`linking ${links.length} files...`))
|
|
|
|
syncLinks(links)
|
|
|
|
const toBeIgnored = links.map(link => link.dest.substr(destDir.length + 1))
|
|
|
|
log(yellow(`adding ${toBeIgnored.length} files to ${ignoreFile}...`))
|
|
|
|
setIgnored(ignoreFile, toBeIgnored)
|
|
|
|
log(green('done!'))
|
|
|
|
} else {
|
|
|
|
log(yellow('(no links to copy)'))
|
|
|
|
}
|
2018-12-07 00:03:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function watch(options) {
|
2018-12-07 00:23:43 +03:00
|
|
|
const {debug = false} = options
|
|
|
|
const keys = Object.keys(map)
|
|
|
|
const globs = keys.map(path => join(sourceDir, path))
|
|
|
|
const log = debug ? console.warn : noop
|
2018-12-07 00:03:41 +03:00
|
|
|
let timeout
|
|
|
|
const update = path => {
|
|
|
|
if (timeout) return
|
|
|
|
timeout = setTimeout(() => {
|
2018-12-07 00:23:43 +03:00
|
|
|
log(`${yellow('changed')} ${path}`)
|
|
|
|
sync(options)
|
2018-12-07 00:03:41 +03:00
|
|
|
clearTimeout(timeout)
|
|
|
|
timeout = null
|
|
|
|
}, 50)
|
|
|
|
}
|
2018-12-07 00:23:43 +03:00
|
|
|
sync(options)
|
|
|
|
log(`watching in ${yellow(sourceDir)}: ${keys.join(', ')}`)
|
2018-12-07 00:03:41 +03:00
|
|
|
return chokidar.watch(globs)
|
2018-12-07 00:23:43 +03:00
|
|
|
// .on('add', update)
|
2018-12-07 00:03:41 +03:00
|
|
|
.on('change', update)
|
|
|
|
.on('unlink', update)
|
|
|
|
}
|
|
|
|
|
|
|
|
function syncLinks(links) {
|
2018-12-07 01:00:24 +03:00
|
|
|
const message = `sync ${links.length} links`
|
|
|
|
console.time(message)
|
2018-12-01 03:17:36 +03:00
|
|
|
for (const {source, dest} of links) {
|
2018-12-07 01:00:24 +03:00
|
|
|
console.warn(`${source.substr(sourceDir.length + 1)} ${yellow('->')} ${dest.substr(destDir.length + 1)}`)
|
|
|
|
const destPath = dirname(dest)
|
2018-12-01 03:17:36 +03:00
|
|
|
removeSync(dest)
|
2018-12-07 01:00:24 +03:00
|
|
|
ensureDirSync(destPath)
|
2018-12-01 03:17:36 +03:00
|
|
|
copySync(source, dest)
|
|
|
|
}
|
2018-12-07 01:00:24 +03:00
|
|
|
console.timeEnd(message)
|
2018-12-01 03:17:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function getLinks(sourceDir, destDir, map) {
|
|
|
|
const links = []
|
|
|
|
|
|
|
|
const mapEntries = Object.entries(map)
|
|
|
|
for (const [source, dest] of mapEntries) {
|
|
|
|
if (source.indexOf('..') === 0 && typeof dest === 'string') {
|
|
|
|
links.push({source, dest})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
console.warn(yellow(`walking: ${sourceDir}...`))
|
|
|
|
const items = klaw(sourceDir, {
|
|
|
|
nodir: true,
|
|
|
|
filter: item => item.path.indexOf('node_modules') === -1
|
|
|
|
})
|
|
|
|
|
|
|
|
let skipped = []
|
|
|
|
for (const item of items) {
|
|
|
|
// item.path is fully-qualified, so we need to remove the sourceDir
|
|
|
|
// from the beginning of it to get the relative path
|
|
|
|
const source = item.path.substr(sourceDir.length + 1)
|
|
|
|
let linked = false
|
|
|
|
for (const [pattern, name] of mapEntries) {
|
|
|
|
if (source === pattern || minimatch(source, pattern)) {
|
|
|
|
const dest = typeof name === 'function' ? name(source) : name
|
|
|
|
if (dest) {
|
|
|
|
links.push({source, dest})
|
|
|
|
linked = true
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!linked && source.endsWith('.md')) {
|
|
|
|
skipped.push(source)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-07 01:00:24 +03:00
|
|
|
skipped = skipped.filter(source => source !== 'README.md')
|
2018-12-01 03:17:36 +03:00
|
|
|
if (skipped.length) {
|
2018-12-07 01:00:24 +03:00
|
|
|
console.warn(`ignored ${yellow(skipped.length)} files`)
|
|
|
|
for (const source of skipped) {
|
|
|
|
console.warn(`${yellow('-')} ${source}`)
|
2018-12-01 03:17:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return links.map(({source, dest}) => ({
|
|
|
|
source: join(sourceDir, source),
|
|
|
|
dest: join(destDir, dest)
|
|
|
|
}))
|
2018-12-07 01:00:24 +03:00
|
|
|
.filter(({source, dest}) => {
|
|
|
|
if (!existsSync(source)) {
|
|
|
|
console.warn(`${red('x')} missing: ${source.substr(sourceDir.length + 1)}`)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
const sourceContent = readFileSync(source, 'utf8')
|
|
|
|
const {data} = matter(sourceContent)
|
|
|
|
if (data.docs === false) {
|
|
|
|
console.warn(`${yellow('x')} ${source.substr(sourceDir.length + 1)} (docs: false)`)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const destContent = readFileSync(dest, 'utf8')
|
|
|
|
return sourceContent !== destContent
|
|
|
|
} catch (error) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
})
|
2018-12-01 03:17:36 +03:00
|
|
|
}
|
2018-12-07 00:03:41 +03:00
|
|
|
|
2018-12-07 00:23:43 +03:00
|
|
|
function shortName(path) {
|
|
|
|
return path.match(/primer-([-\w]+)/)[1]
|
|
|
|
}
|
|
|
|
|
2018-12-07 00:03:41 +03:00
|
|
|
function noop() {
|
|
|
|
}
|