diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..b2095be --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "semi": false, + "singleQuote": true +} diff --git a/examples/blog/peco.config.js b/examples/blog/peco.config.js index 449a7e5..8660693 100644 --- a/examples/blog/peco.config.js +++ b/examples/blog/peco.config.js @@ -1,20 +1,3 @@ -const axios = require('axios') - -const routes = [ - { - component: 'components/posts.vue', - path: '/posts', - props: async () => { - const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts', { - timeout: 3000 - }) - return { - posts: data - } - } - } -] - module.exports = { title: 'Peco', description: 'Humbly powered by Peco!!!', @@ -26,9 +9,5 @@ module.exports = { title: '佩可', // override root title option description: '低调低使用佩可驱动' // override root description option } - }, - - plugins: [ - ['../../plugins/routes', routes] - ] + } } diff --git a/examples/blog/source/_posts/another-post.md b/examples/blog/source/_posts/another-post.md index dd434b5..172836a 100644 --- a/examples/blog/source/_posts/another-post.md +++ b/examples/blog/source/_posts/another-post.md @@ -2,7 +2,7 @@ title: Another Post --- -Commodo aliquip ea incididunt minim anim eiusmod anim ipsum deserunt laboris. Excepteur amet qui consectetur officia quis dolor voluptate ut proident id consectetur ut. Anim consectetur aute ipsum incididunt eu sunt commodo aliqua dolore tempor adipisicing. Commodo eu consectetur ullamco in duis. +__Commodo__ aliquip ea incididunt minim anim eiusmod anim ipsum deserunt laboris. Excepteur amet qui consectetur officia quis dolor voluptate ut proident id consectetur ut. Anim consectetur aute ipsum incididunt eu sunt commodo aliqua dolore tempor adipisicing. Commodo eu consectetur ullamco in duis. Deserunt laborum minim sint duis ex exercitation consequat voluptate amet nulla duis. Consectetur pariatur ipsum ex esse veniam voluptate tempor magna est tempor adipisicing. Sunt elit irure sit et minim. diff --git a/lib/index.js b/lib/index.js index 5033e2b..75e7ae2 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,7 +4,6 @@ const { promisify, inspect } = require('util') const chokidar = require('chokidar') const chalk = require('chalk') const config = require('./utils/config') -const frontMatter = require('./utils/front-matter') const localRequire = require('./utils/local-require') class Peco { @@ -21,70 +20,23 @@ class Peco { this.hooks = require('./hooks') // All markdown files - this.files = new Map() this.routes = new Map() this.config = {} this.enhanceAppFiles = new Set() } use(plugin, options) { + let Plugin if (typeof plugin === 'string') { - localRequire.require(plugin)(this, options) - } else { - plugin(this, options) + Plugin = localRequire.require(plugin) + } else if (typeof plugin === 'function') { + Plugin = plugin } + const plug = new Plugin(options) + plug.apply(this) return this } - getPermalink(filepath, { date, type } = {}) { - // slugified filename - let slug = encodeURI( - filepath.replace(/^_posts\//, '').replace(/\.md$/, '') - ) - - if (type === 'post') { - const d = new Date(date) - const year = d.getFullYear() - const iMonth = d.getMonth() + 1 - const iDay = d.getDate() - const minutes = d.getMinutes() - const seconds = d.getSeconds() - const month = iMonth < 10 ? `0${iMonth}` : iMonth - const day = iDay < 10 ? `0${iDay}` : iDay - - let langPrefix = '' - - if (this.config.localeNames) { - for (const name of this.config.localeNames) { - const RE = new RegExp(`^${name}[/$]`) - if (RE.test(slug)) { - slug = slug.replace(RE, '') - // Do not add lang prefix for default locale - // eslint-disable-next-line max-depth - if (name !== this.config.defaultLocale) { - langPrefix = `${name}/` - } - break - } - } - } - - const link = langPrefix + this.config.permalink - .replace(/:year/, year) - .replace(/:month/, month) - .replace(/:i_month/, iMonth) - .replace(/:i_day/, iDay) - .replace(/:day/, day) - .replace(/:minutes/, minutes) - .replace(/:seconds/, seconds) - .replace(/:slug/, slug) - - return link.replace(/^\/?/, '/') - } - - return require('./utils/handle-index-route')('/' + slug) - } - applyPlugins() { const { plugins } = this.config if (!plugins) return @@ -161,7 +113,7 @@ class Peco { // Initialize plugins // Plugin to write index file - this.use(require('../plugins/write-index')) + this.use(require('../plugins/source-file-system')) this.use(require('../plugins/google-analytics'), this.config.googleAnalytics) this.applyPlugins() @@ -177,59 +129,6 @@ class Peco { // Clean .peco directory await fs.emptyDir(this.resolvePecoDir()) - // Matches all markdown files - // But excluding folders starting with and underscore - // Excepte for `_posts` - const globs = ['**/*.md', '!**/_!(posts)/*.md'] - const fileStats = await require('fast-glob')(globs, { - cwd: path.join(this.options.baseDir, this.config.sourceDir), - stats: true - }) - - await this.onInitFiles( - new Map( - [ - // Add a default index.md file - ['index.md', { - isVirtual: true, - data: { - attributes: { - layout: 'index', - type: 'index' - }, - permalink: '/', - slug: '' - } - }], - ...fileStats.map(stats => { - return [stats.path, { stats }] - }) - ] - ) - ) - - await this.hooks.runParallel('onPrepare') - - // Watch files in dev mode, and re-`buildData` and/or re-`prepare` - if (dev) { - const filesWatcher = chokidar.watch(globs, { - cwd: path.join(this.options.baseDir, this.config.sourceDir), - ignoreInitial: true - }) - - filesWatcher - .on('add', async filepath => { - await this.onAddFile(filepath).catch(console.error) - }) - .on('unlink', async filepath => { - await this.onDeleteFile(filepath).catch(console.error) - }) - .on('change', async filepath => { - // Use cached stats since we only need path and birthtime for now - await this.onChangeFile(filepath).catch(console.error) - }) - } - // Build Peco config as JSON file // So themes could load it at build time via `import` await this.buildSiteData() @@ -242,6 +141,8 @@ class Peco { .on('change', filepath => this.buildSiteData(filepath)) .on('unlink', filepath => this.buildSiteData(filepath)) } + + await this.hooks.runParallel('onPrepare') } // TODO: really should clean up this mess @@ -253,48 +154,6 @@ class Peco { this.config = configData this.siteData = siteData - - const Markdown = require('markdown-it') - this.markdown = new Markdown({ - html: true, - linkify: true, - highlight: require('./markdown/highlight') - }) - this.markdown.use(require('./markdown/excerpt')) - - const { markdown } = configData - - if (markdown.highlightLines !== false) { - this.markdown.use(require('markdown-it-highlight-lines')) - } - - if (markdown.anchor !== false) { - let slugify - if (typeof markdown.slugify === 'string') { - slugify = localRequire.require(markdown.slugify) - } else if (typeof markdown.slugify === 'function') { - slugify = markdown.slugify - } - this.markdown.use( - require('markdown-it-anchor'), - Object.assign( - { - slugify - }, - markdown.anchor - ) - ) - } - - if (markdown.plugins) { - if (typeof markdown.plugins === 'function') { - markdown.plugins(this.markdown) - } else if (Array.isArray(markdown.plugins)) { - for (const plugin of markdown.plugins) { - this.markdown.use(localRequire.require(plugin.name), plugin.options) - } - } - } } chainWebpack(fn) { @@ -318,170 +177,6 @@ class Peco { return this.resolveBaseDir(this.config.sourceDir, ...args) } - async getFileData(filepath, stats) { - const content = await fs.readFile( - path.join(this.options.baseDir, this.config.sourceDir, filepath), - 'utf8' - ) - const { attributes, body } = frontMatter(content) - - const env = {} - - // Ensure page layout and type - const setLayout = layout => { - if (typeof attributes.layout !== 'string') { - attributes.layout = layout - } - } - const setType = type => { - if (typeof attributes.type !== 'string') { - attributes.type = type - } - } - - if (filepath === 'index.md') { - setLayout('index') - setType('index') - } else if (filepath.startsWith('_posts/')) { - setLayout('post') - setType('post') - } else { - setLayout('page') - setType('page') - } - - // Ensure page date - // Default to the creation time of the file - attributes.date = attributes.date || stats.birthtime - - const permalink = this.getPermalink(filepath, { - type: attributes.layout, - date: attributes.date - }) - - const data = { - slug: permalink.slice(1), - permalink, - attributes, - body: this.markdown.render(body, env), - excerpt: env.excerpt - } - return data - } - - addRouteFromPath(filepath, route) { - if (route) { - return this.routes.set(route, { - path: filepath, - type: 'peson' - }) - } - - const file = this.files.get(filepath) - if (file.data.attributes.type !== 'index') { - this.routes.set(file.data.permalink, { - path: `dot-peco/data${file.data.permalink}.peson`, - type: 'peson' - }) - } - } - - removeRouteByPath(filepath) { - for (const [_, item] of this.routes.entries()) { - if (item.path === filepath) { - this.routes.delete(_) - break - } - } - } - - async onDeleteFile(filepath) { - this.files.delete(filepath) - this.removeRouteByPath(filepath) - await this.buildFiles() - await this.hooks.runParallel('onRoutesUpdate') - } - - async onAddFile(filepath) { - const stats = await fs.stat( - path.join(this.options.baseDir, this.config.sourceDir, filepath) - ) - - this.files.set(filepath, { - stats, - data: await this.getFileData(filepath, stats) - }) - this.addRouteFromPath(filepath) - await this.buildFiles({ filepath }) - await this.hooks.runParallel('onRoutesUpdate') - } - - async onChangeFile(filepath) { - const stats = await fs.stat( - this.resolveSourceDir(filepath) - ) - this.files.set(filepath, { - stats, - data: await this.getFileData(filepath, stats) - }) - await this.buildFiles({ filepath }) - // For now no need to update route for changed files - // Since we get route from filepath - // But in the future you may use `slug` in front-matter to set the route - } - - async onInitFiles(files) { - this.files = files - - await this.buildFiles() - for (const filepath of files.keys()) { - this.addRouteFromPath(filepath) - } - - await this.hooks.runParallel('onInitFiles') - // The files data is ready so tell the engine about that - // e.g. vue-engine will create a router.js - await this.hooks.runParallel('onRoutesUpdate') - } - - async buildFiles({ filepath } = {}) { - const files = filepath ? - [[filepath, this.files.get(filepath)]] : - Array.from(this.files.entries()) - - await Promise.all( - files.map(async ([filepath, file]) => { - // eslint-disable-next-line no-multi-assign - const data = file.isVirtual ? file.data : await this.getFileData( - filepath, - file.stats - ) - - this.files.get(filepath).data = data - - // Write data for index type later - if (data.attributes.type === 'index') { - return - } - - const outFile = this.resolvePecoDir('data', `${data.permalink}.peson`) - await fs.ensureDir(path.dirname(outFile)) - await fs.writeFile(outFile, JSON.stringify(data), 'utf8') - }) - ) - // Sort posts - const posts = [...this.files.values()] - .filter(file => file.data.attributes.layout === 'post') - .map(v => v.data) - .sort((a, b) => { - return new Date(a.attributes.date) > new Date(b.attributes.date) ? - -1 : - 1 - }) - - await this.hooks.runParallel('buildFiles', posts) - } - // TODO: Restart when some options like `source` `theme` changed` async buildSiteData(filepath) { // When the config file is a .js file diff --git a/plugins/google-analytics/index.js b/plugins/google-analytics/index.js index 7331456..02cb7dd 100644 --- a/plugins/google-analytics/index.js +++ b/plugins/google-analytics/index.js @@ -1,15 +1,19 @@ const path = require('path') -module.exports = (api, options) => { - const ga = typeof options === 'string' ? { id: options } : options || {} +module.exports = class GoogleAnalyticsPlugin { + constructor(options) { + this.ga = typeof options === 'string' ? { id: options } : options || {} + } - api.chainWebpack(config => { - config.plugin('constants').tap(([options]) => [ - Object.assign(options, { - __GA_ID__: ga.id ? JSON.stringify(ga.id) : false - }) - ]) - }) + apply(api) { + api.chainWebpack(config => { + config.plugin('constants').tap(([options]) => [ + Object.assign(options, { + __GA_ID__: this.ga.id ? JSON.stringify(this.ga.id) : false + }) + ]) + }) - api.enhanceAppFiles.add(path.join(__dirname, 'google-analytics-inject.js')) + api.enhanceAppFiles.add(path.join(__dirname, 'google-analytics-inject.js')) + } } diff --git a/plugins/pages.js b/plugins/pages.js deleted file mode 100644 index 63c2f0b..0000000 --- a/plugins/pages.js +++ /dev/null @@ -1,48 +0,0 @@ -const path = require('path') -const chokidar = require('chokidar') - -module.exports = api => { - api.hooks.add('onInitFiles', async () => { - const globs = ['**/*.{vue,js}', '!**/node_modules/*', '!**/_*'] - const pagesDir = path.join(api.options.baseDir, 'pages') - const pages = await require('fast-glob')(globs, { - cwd: pagesDir - }) - - if (pages.length > 0) { - console.log('> Building from pages') - } - - const addRoute = async filepath => { - api.routes.set(api.getPermalink(filepath), { - path: `@base/pages/${filepath}`, - type: 'component' - }) - } - - const deleteRoute = async filepath => { - const route = api.getPermalink(filepath) - api.route.delete(route) - await api.hooks.runParallel('onRoutesUpdate') - } - - for (const page of pages) { - addRoute(page) - } - await api.hooks.runParallel('onRoutesUpdate') - - if (api.mode !== 'development') return - - const watcher = chokidar.watch(globs, { - ignoreInitial: true, - cwd: pagesDir - }) - - watcher.on('add', async filepath => { - addRoute(filepath) - await api.hooks.runParallel('onRoutesUpdate') - }).on('unlink', filepath => { - deleteRoute(filepath) - }) - }) -} diff --git a/plugins/routes.js b/plugins/routes.js deleted file mode 100644 index 2db64fc..0000000 --- a/plugins/routes.js +++ /dev/null @@ -1,22 +0,0 @@ -const fs = require('fs-extra') - -module.exports = (api, routes) => { - if (!routes) return - - api.hooks.add('onInitFiles', async () => { - console.log('> Fetching data for routes') - if (typeof routes === 'function') { - routes = await routes() - } - await Promise.all(routes.map(async (route, index) => { - const data = route.props ? await route.props() : null - const outFile = api.resolvePecoDir('data', `__routes_prefetch_${index}.json`) - await fs.writeFile(outFile, JSON.stringify(data), 'utf8') - api.routes.set(route.path, { - type: 'component', - path: `@base/${route.component}`, - prefetchFile: outFile - }) - })) - }) -} diff --git a/lib/utils/front-matter.js b/plugins/source-file-system/front-matter.js similarity index 100% rename from lib/utils/front-matter.js rename to plugins/source-file-system/front-matter.js diff --git a/lib/utils/get-page-link.js b/plugins/source-file-system/get-page-link.js similarity index 100% rename from lib/utils/get-page-link.js rename to plugins/source-file-system/get-page-link.js diff --git a/plugins/source-file-system/index.js b/plugins/source-file-system/index.js new file mode 100644 index 0000000..5f269d7 --- /dev/null +++ b/plugins/source-file-system/index.js @@ -0,0 +1,293 @@ +const path = require('path') +const glob = require('fast-glob') +const fs = require('fs-extra') +const chokidar = require('chokidar') +const frontMatter = require('./front-matter') +const localRequire = require('../../lib/utils/local-require') + +module.exports = class SourceFileSystem { + apply(api) { + this.api = api + + // Markdown should be optional in the future + // We should be able to use current parser like asciidoc + this.setMarkdown() + + require('./write-index')(api, this) + + api.hooks.add('onPrepare', async () => { + const globs = ['**/*.md', '!**/_!(posts)/*.md'] + const fileStats = await glob(globs, { + cwd: path.join(api.options.baseDir, api.config.sourceDir), + stats: true + }) + const files = new Map([ + ...fileStats.map(stats => { + return [stats.path, { stats }] + }) + ]) + + await this.onInitFiles(files) + + // Watch files in dev mode, and re-`buildData` and/or re-`prepare` + if (api.mode === 'development') { + const filesWatcher = chokidar.watch(globs, { + cwd: path.join(api.options.baseDir, api.config.sourceDir), + ignoreInitial: true + }) + + filesWatcher + .on('add', async filepath => { + await this.onAddFile(filepath).catch(console.error) + }) + .on('unlink', async filepath => { + await this.onDeleteFile(filepath).catch(console.error) + }) + .on('change', async filepath => { + // Use cached stats since we only need path and birthtime for now + await this.onChangeFile(filepath).catch(console.error) + }) + } + }) + } + + addRouteFromPath(filepath, route) { + if (route) { + return this.api.routes.set(route, { + path: filepath, + type: 'peson' + }) + } + + const file = this.files.get(filepath) + this.api.routes.set(file.data.permalink, { + path: `dot-peco/data/${filepath}.peson`, + type: 'peson' + }) + } + + removeRouteByPath(filepath) { + for (const [_, item] of this.api.routes.entries()) { + if (item.path === filepath) { + this.api.routes.delete(_) + break + } + } + } + + setMarkdown() { + const Markdown = require('markdown-it') + + this.markdown = new Markdown({ + html: true, + linkify: true, + highlight: require('./markdown/highlight') + }) + this.markdown.use(require('./markdown/excerpt')) + + const { markdown } = this.api.config + + if (markdown.highlightLines !== false) { + this.markdown.use(require('markdown-it-highlight-lines')) + } + + if (markdown.anchor !== false) { + let slugify + if (typeof markdown.slugify === 'string') { + slugify = localRequire.require(markdown.slugify) + } else if (typeof markdown.slugify === 'function') { + slugify = markdown.slugify + } + this.markdown.use( + require('markdown-it-anchor'), + Object.assign( + { + slugify + }, + markdown.anchor + ) + ) + } + + if (markdown.plugins) { + if (typeof markdown.plugins === 'function') { + markdown.plugins(this.markdown) + } else if (Array.isArray(markdown.plugins)) { + for (const plugin of markdown.plugins) { + this.markdown.use(localRequire.require(plugin.name), plugin.options) + } + } + } + } + + async buildFiles({ filepath } = {}) { + const files = filepath ? + [[filepath, this.files.get(filepath)]] : + Array.from(this.files.entries()) + + await Promise.all( + files.map(async ([filepath, file]) => { + // eslint-disable-next-line no-multi-assign + const data = file.isVirtual ? + file.data : + await this.getFileData(filepath, file.stats) + + this.files.get(filepath).data = data + + const outFile = this.api.resolvePecoDir('data', `${filepath}.peson`) + await fs.ensureDir(path.dirname(outFile)) + await fs.writeFile(outFile, JSON.stringify(data), 'utf8') + }) + ) + + // Return sorted posts + return [...this.files.values()] + .filter(file => file.data.attributes.layout === 'post') + .map(v => v.data) + .sort((a, b) => { + return new Date(a.attributes.date) > new Date(b.attributes.date) ? + -1 : + 1 + }) + } + + async getFileData(filepath, stats) { + const content = await fs.readFile( + path.join(this.api.options.baseDir, this.api.config.sourceDir, filepath), + 'utf8' + ) + const { attributes, body } = frontMatter(content) + + const env = {} + + // Ensure page layout and type + const setLayout = layout => { + if (typeof attributes.layout !== 'string') { + attributes.layout = layout + } + } + const setType = type => { + if (typeof attributes.type !== 'string') { + attributes.type = type + } + } + + if (filepath === 'index.md') { + setLayout('index') + setType('index') + } else if (filepath.startsWith('_posts/')) { + setLayout('post') + setType('post') + } else { + setLayout('page') + setType('page') + } + + // Ensure page date + // Default to the creation time of the file + attributes.date = attributes.date || stats.birthtime + + const permalink = this.getPermalink(filepath, { + type: attributes.type, + date: attributes.date + }) + + const data = { + slug: permalink.slice(1), + permalink, + attributes, + body: this.markdown.render(body, env), + excerpt: env.excerpt + } + return data + } + + getPermalink(filepath, { date, type } = {}) { + // slugified filename + let slug = encodeURI(filepath.replace(/^_posts\//, '').replace(/\.md$/, '')) + + if (type === 'post') { + const d = new Date(date) + const year = d.getFullYear() + const iMonth = d.getMonth() + 1 + const iDay = d.getDate() + const minutes = d.getMinutes() + const seconds = d.getSeconds() + const month = iMonth < 10 ? `0${iMonth}` : iMonth + const day = iDay < 10 ? `0${iDay}` : iDay + + let langPrefix = '' + + if (this.api.config.localeNames) { + for (const name of this.api.config.localeNames) { + const RE = new RegExp(`^${name}[/$]`) + if (RE.test(slug)) { + slug = slug.replace(RE, '') + // Do not add lang prefix for default locale + // eslint-disable-next-line max-depth + if (name !== this.api.config.defaultLocale) { + langPrefix = `${name}/` + } + break + } + } + } + + const link = + langPrefix + + this.api.config.permalink + .replace(/:year/, year) + .replace(/:month/, month) + .replace(/:i_month/, iMonth) + .replace(/:i_day/, iDay) + .replace(/:day/, day) + .replace(/:minutes/, minutes) + .replace(/:seconds/, seconds) + .replace(/:slug/, slug) + + return link.replace(/^\/?/, '/') + } + + const removeIndexSuffix = route => { + if (route.endsWith('/index')) { + return route.replace(/\/index$/, '/') + } + return route + } + + return removeIndexSuffix('/' + slug) + } + + async onInitFiles(files) { + this.files = files + + const posts = await this.buildFiles() + for (const filepath of files.keys()) { + this.addRouteFromPath(filepath) + } + + await this.api.hooks.runParallel('onBuildFiles', posts) + await this.api.hooks.runParallel('onRoutesUpdate') + } + + async onAddFile(filepath) { + const posts = await this.buildFiles({ filepath }) + this.addRouteFromPath(filepath) + await this.api.hooks.runParallel('onBuildFiles', posts) + await this.api.hooks.runParallel('onRoutesUpdate') + } + + async onChangeFile(filepath) { + await this.buildFiles({ filepath }) + } + + // Current implement is stupid + // It rebuilds everything + // We should only rebuild files that are related to the deleted file + // e.g. the router and relevant category/tag/index page + async onDeleteFile(filepath) { + this.removeRouteByPath(filepath) + await this.buildFiles() + await this.api.hooks.runParallel('onRoutesUpdate') + } +} diff --git a/lib/markdown/__test__/__snapshots__/excerpt.test.js.snap b/plugins/source-file-system/markdown/__test__/__snapshots__/excerpt.test.js.snap similarity index 100% rename from lib/markdown/__test__/__snapshots__/excerpt.test.js.snap rename to plugins/source-file-system/markdown/__test__/__snapshots__/excerpt.test.js.snap diff --git a/lib/markdown/__test__/excerpt.test.js b/plugins/source-file-system/markdown/__test__/excerpt.test.js similarity index 100% rename from lib/markdown/__test__/excerpt.test.js rename to plugins/source-file-system/markdown/__test__/excerpt.test.js diff --git a/lib/markdown/excerpt.js b/plugins/source-file-system/markdown/excerpt.js similarity index 100% rename from lib/markdown/excerpt.js rename to plugins/source-file-system/markdown/excerpt.js diff --git a/lib/markdown/highlight.js b/plugins/source-file-system/markdown/highlight.js similarity index 100% rename from lib/markdown/highlight.js rename to plugins/source-file-system/markdown/highlight.js diff --git a/plugins/write-index.js b/plugins/source-file-system/write-index.js similarity index 84% rename from plugins/write-index.js rename to plugins/source-file-system/write-index.js index 71fbf50..46ab3dd 100644 --- a/plugins/write-index.js +++ b/plugins/source-file-system/write-index.js @@ -1,6 +1,6 @@ const path = require('path') const fs = require('fs-extra') -const getPageLink = require('../lib/utils/get-page-link') +const getPageLink = require('./get-page-link') const hasMatchedLang = (langs, slug) => { return langs.some(lang => { @@ -18,11 +18,11 @@ const addIndexSuffix = route => { return route.endsWith('/') ? `${route}index` : route } -module.exports = api => { - api.hooks.add('buildFiles', async _posts => { +module.exports = (api, plugin) => { + api.hooks.add('onBuildFiles', async _posts => { // Write index layout files await Promise.all( - Array.from(api.files.entries()).map(async entry => { + Array.from(plugin.files.entries()).map(async entry => { // Make a copy of these posts to manipulate in parellel let posts = [..._posts] @@ -52,13 +52,8 @@ module.exports = api => { } if (posts.length === 0) { - const outFile = api.resolvePecoDir( - 'data', - `${addIndexSuffix(data.permalink)}.peson` - ) - api.addRouteFromPath(outFile, data.permalink) - await fs.ensureDir(path.dirname(outFile)) - await fs.writeFile(outFile, JSON.stringify(data), 'utf8') + plugin.addRouteFromPath(filepath, data.permalink) + await fs.writeFile(filepath, JSON.stringify(data), 'utf8') return } @@ -79,7 +74,7 @@ module.exports = api => { 'data', `${addIndexSuffix(route)}.peson` ) - api.addRouteFromPath( + plugin.addRouteFromPath( outFile.replace(api.resolvePecoDir(), 'dot-peco'), route )