UNPKG

4.54 kBJavaScriptView Raw
1'use strict'
2const updateNotifier = require('update-notifier')
3const meow = require('meow')
4const processFiles = require('./process-files')
5const normalizeNewline = require('normalize-newline')
6const relative = require('relative')
7const normalizePath = require('normalize-path')
8const chalk = require('chalk')
9const fs = require('fs')
10const path = require('path')
11const test = require('tape')
12const tapDiff = require('@zkochan/tap-diff')
13const readPkgUp = require('@zkochan/read-pkg-up')
14const mos = require('mos-processor')
15const defaultPlugins = require('./default-plugins')
16const resolve = require('resolve')
17
18const cwd = process.cwd()
19const stdout = process.stdout
20
21const cli = meow([
22 'Usage',
23 ' mos [test] [files]',
24 '',
25 'Options',
26 ' --init Add mos to your project',
27 ' --help, -h Display usage',
28 ' --version, -v Display version',
29 ' --tap Generate TAP output when testing markdown files',
30 '',
31 ' -x="<exclude-pattern>"',
32 ' Exclude pattern',
33 '',
34 'Examples',
35 ' mos',
36 ' mos test',
37 ' mos test README.md',
38 ' mos --init',
39 ' mos --init README.md',
40 '',
41 'Tips:',
42 ' Add `mos test` to your `scripts.test` property in `package.json`',
43].join('\n'), {
44 alias: {
45 help: 'h',
46 version: 'v',
47 },
48})
49
50updateNotifier({pkg: cli.pkg}).notify()
51
52if (cli.flags.init) {
53 require('mos-init')()
54 return // eslint-disable-line
55}
56
57const highlightPath = chalk.bgBlack.yellow
58
59const isTest = ~['test', 't'].indexOf((cli.input[0] || '').toLowerCase())
60
61const processMD = md => readPkgUp({ cwd: md.filePath })
62 .then(result => {
63 const allDeps = new Set(Object
64 .keys(result.pkg.dependencies || {})
65 .concat(Object.keys(result.pkg.devDependencies || {})))
66
67 result.pkg.mos = result.pkg.mos || {}
68
69 const pkgPlugins = (result.pkg.mos.plugins || [])
70 .map(plugin => plugin instanceof Array
71 ? { name: plugin[0], options: plugin[1] || {} }
72 : { name: plugin, options: {}}
73 )
74 .map(plugin => {
75 const namespacedName = 'mos-plugin-' + plugin.name
76 if (allDeps.has(namespacedName)) {
77 return Object.assign({}, plugin, {name: namespacedName})
78 }
79 if (allDeps.has(plugin.name)) {
80 return plugin
81 }
82 throw new Error(`${plugin.name} is not in the dependencies`)
83 })
84 .map(plugin => Object.assign(plugin, {path: resolve.sync(plugin.name, { basedir: path.dirname(md.filePath) })}))
85 .map(plugin => Object.assign(plugin, {path: normalizePath(plugin.path)}))
86 .map(plugin => Object.assign(plugin, {register: require(plugin.path)}))
87
88 const defaultPluginsWithOpts = defaultPlugins.reduce((defPlugins, defPlugin) => {
89 const defPluginName = defPlugin.attributes.pkg && defPlugin.attributes.pkg.name || defPlugin.attributes.name
90 const options = result.pkg.mos[defPluginName] || result.pkg.mos[defPluginName.replace(/^mos-plugin-/, '')]
91 if (options === false) {
92 return defPlugins
93 }
94 return defPlugins.concat({
95 register: defPlugin,
96 options: options
97 })
98 }, [])
99
100 return mos(md, defaultPluginsWithOpts.concat(pkgPlugins))
101 })
102 .then(processor => processor.process())
103
104const mdExtensions = ['markdown', 'mdown', 'mkdn', 'mkd', 'md']
105const files = cli.input[isTest ? 1 : 0]
106const pattern = files
107 ? path.resolve(cwd, files)
108 : path.resolve(cwd, `{/**/,/}*.{${mdExtensions.join()}}`)
109const ignorePattern = cli.flags.x
110 ? path.resolve(cwd, cli.flags.x)
111 : null
112
113if (isTest) {
114 if (cli.flags.tap !== true) {
115 test.createStream()
116 .pipe(tapDiff())
117 .pipe(stdout)
118 }
119 test('markdown', t => {
120 processFiles({
121 process: processMD,
122 pattern,
123 ignorePattern,
124 afterEachRender (opts) {
125 const relativePath = normalizePath(getRelativePath(opts.filePath))
126 t.equal(normalizeNewline(opts.newMD), normalizeNewline(opts.currentMD), relativePath)
127 },
128 })
129 .then(() => t.end())
130 .catch(err => { throw err })
131 })
132 return
133}
134processFiles({
135 process: processMD,
136 pattern,
137 ignorePattern,
138 afterEachRender (opts) {
139 if (normalizeNewline(opts.newMD) !== normalizeNewline(opts.currentMD)) {
140 fs.writeFileSync(opts.filePath, opts.newMD, {
141 encoding: 'utf8',
142 })
143 const relativePath = normalizePath(getRelativePath(opts.filePath))
144 console.log('updated', highlightPath(relativePath))
145 }
146 },
147})
148.catch(err => { throw err })
149
150function getRelativePath (filePath) {
151 return relative(cwd, filePath)
152}