diff --git a/other-packages/eslint-config-saber/index.js b/other-packages/eslint-config-saber/index.js
index 2a1aee8d7..8191e3b73 100644
--- a/other-packages/eslint-config-saber/index.js
+++ b/other-packages/eslint-config-saber/index.js
@@ -36,6 +36,7 @@ module.exports = {
requireLast: false
}
}
- ]
+ ],
+ '@typescript-eslint/prefer-includes': 'off'
}
}
diff --git a/packages/saber/src/Compiler.ts b/packages/saber/src/Compiler.ts
index b939b1711..0ffdf4063 100644
--- a/packages/saber/src/Compiler.ts
+++ b/packages/saber/src/Compiler.ts
@@ -17,6 +17,7 @@ export class Compiler extends EventEmitter {
injectToWebpack(config: WebpackChain) {
const ID = `compiler-${this.type}`
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this
config.plugin(ID).use(
class {
diff --git a/packages/saber/src/plugins/config-css.js b/packages/saber/src/plugins/config-css.js
index 2d934785b..3d0e8f8be 100644
--- a/packages/saber/src/plugins/config-css.js
+++ b/packages/saber/src/plugins/config-css.js
@@ -150,7 +150,7 @@ exports.apply = api => {
name: 'styles',
// necessary to ensure async chunks are also extracted
test: m => {
- return /css\/mini-extract/.test(m.type)
+ return m.type && m.type.includes('css/mini-extract')
},
chunks: 'all',
enforce: true
diff --git a/packages/saber/src/plugins/emit-runtime-polyfills.js b/packages/saber/src/plugins/emit-runtime-polyfills.js
index a5289ef85..7a8d76dd8 100644
--- a/packages/saber/src/plugins/emit-runtime-polyfills.js
+++ b/packages/saber/src/plugins/emit-runtime-polyfills.js
@@ -18,6 +18,7 @@ exports.apply = api => {
polyfills,
'utf8'
)
+ // eslint-disable-next-line require-atomic-updates
previousPolyfills = polyfills
}
})
diff --git a/packages/saber/src/plugins/extend-browser-api.js b/packages/saber/src/plugins/extend-browser-api.js
index 43ad66135..e608fd9d9 100644
--- a/packages/saber/src/plugins/extend-browser-api.js
+++ b/packages/saber/src/plugins/extend-browser-api.js
@@ -24,14 +24,20 @@ exports.apply = api => {
cwd: api.resolveCwd(),
ignoreInitial: true
})
+ const onAdd = async filename => {
+ api.browserApi.add(api.resolveCwd(filename))
+ await api.browserApi.reload()
+ }
+ const onRemove = async filename => {
+ api.browserApi.delete(api.resolveCwd(filename))
+ await api.browserApi.reload()
+ }
watcher
- .on('add', async filename => {
- api.browserApi.add(api.resolveCwd(filename))
- await api.browserApi.reload()
+ .on('add', filename => {
+ onAdd(filename)
})
- .on('unlink', async filename => {
- api.browserApi.delete(api.resolveCwd(filename))
- await api.browserApi.reload()
+ .on('unlink', filename => {
+ onRemove(filename)
})
}
})
diff --git a/packages/saber/src/plugins/extend-node-api.js b/packages/saber/src/plugins/extend-node-api.js
index cdd087045..38e16e20c 100644
--- a/packages/saber/src/plugins/extend-node-api.js
+++ b/packages/saber/src/plugins/extend-node-api.js
@@ -4,6 +4,10 @@ const { log, colors } = require('saber-log')
const ID = 'builtin:extend-node-api'
+function __noopHandler__(arg) {
+ return arg
+}
+
exports.name = ID
exports.apply = api => {
@@ -58,35 +62,38 @@ exports.apply = api => {
}
})
- if (api.dev && !/node_modules/.test(nodeApiFile)) {
+ if (api.dev && !nodeApiFile.includes('node_modules')) {
+ const onChange = async action => {
+ updateNodeApi()
+ // Remove all child pages
+ api.pages.removeWhere(page => page.internal.parent)
+ await Promise.all(
+ [...api.pages.values()].map(async page => {
+ // Recreate the page
+ api.pages.createPage(page)
+ // A page has been created
+ await api.hooks.onCreatePage.promise(page)
+ })
+ )
+ // All pages are created
+ await api.hooks.onCreatePages.promise()
+ // Emit pages
+ await api.hooks.emitPages.promise()
+ // Emit route file
+ await api.hooks.emitRoutes.promise()
+ log.warn(
+ `${action[0].toUpperCase()}${action.substring(1)} ${nodeApiFile}`
+ )
+ // Because you might also update webpack config in saber-node.js
+ // Which we can't (?) automatically reload
+ log.warn(`You probably need to restart the server.`)
+ }
require('chokidar')
.watch(nodeApiFile, {
ignoreInitial: true
})
- .on('all', async action => {
- await updateNodeApi()
- // Remove all child pages
- api.pages.removeWhere(page => page.internal.parent)
- await Promise.all(
- [...api.pages.values()].map(async page => {
- // Recreate the page
- api.pages.createPage(page)
- // A page has been created
- await api.hooks.onCreatePage.promise(page)
- })
- )
- // All pages are created
- await api.hooks.onCreatePages.promise()
- // Emit pages
- await api.hooks.emitPages.promise()
- // Emit route file
- await api.hooks.emitRoutes.promise()
- log.warn(
- `${action[0].toUpperCase()}${action.substring(1)} ${nodeApiFile}`
- )
- // Because you might also update webpack config in saber-node.js
- // Which we can't (?) automatically reload
- log.warn(`You probably need to restart the server.`)
+ .on('all', action => {
+ onChange(action)
})
}
}
@@ -94,7 +101,3 @@ exports.apply = api => {
handleNodeApiFile(path.join(api.theme, 'saber-node.js'), 'theme-node-api')
handleNodeApiFile(api.resolveCwd('saber-node.js'), 'user-node-api')
}
-
-function __noopHandler__(arg) {
- return arg
-}
diff --git a/packages/saber/src/plugins/layouts.js b/packages/saber/src/plugins/layouts.js
index c617a7bbe..0853f3b65 100644
--- a/packages/saber/src/plugins/layouts.js
+++ b/packages/saber/src/plugins/layouts.js
@@ -58,6 +58,25 @@ exports.apply = api => {
const watchLayouts = (dir, layouts) => {
const chokidar = require('chokidar')
+ const onRemoveDir = async dir => {
+ if (!dir) {
+ Object.keys(layouts).forEach(name => {
+ delete layouts[name]
+ })
+ await writeLayouts(themeLayouts, userLayouts)
+ }
+ }
+
+ const onAddLayout = async file => {
+ setLayout(layouts, path.join(dir, file))
+ await writeLayouts(themeLayouts, userLayouts)
+ }
+
+ const onRemoveLayout = async file => {
+ setLayout(layouts, path.join(dir, file), true)
+ await writeLayouts(themeLayouts, userLayouts)
+ }
+
// Clear the layouts object when the layouts directory is removed
chokidar
.watch('.', {
@@ -68,30 +87,23 @@ exports.apply = api => {
},
ignoreInitial: true
})
- .on('unlinkDir', async dir => {
- if (!dir) {
- Object.keys(layouts).forEach(name => {
- delete layouts[name]
- })
- await writeLayouts(themeLayouts, userLayouts)
- }
+ .on('unlinkDir', dir => {
+ onRemoveDir(dir)
})
// Add/Remove layout components
chokidar
.watch('*.{vue,js}', { cwd: dir, ignoreInitial: true })
- .on('add', async file => {
- setLayout(layouts, path.join(dir, file))
- await writeLayouts(themeLayouts, userLayouts)
+ .on('add', file => {
+ onAddLayout(file)
})
- .on('unlink', async file => {
- setLayout(layouts, path.join(dir, file), true)
- await writeLayouts(themeLayouts, userLayouts)
+ .on('unlink', file => {
+ onRemoveLayout(file)
})
}
// No need to watch theme layouts if it's from an npm package
- if (!/node_modules/.test(themeLayoutsDir)) {
+ if (!themeLayoutsDir.includes('node_modules')) {
watchLayouts(themeLayoutsDir, themeLayouts)
}
diff --git a/packages/saber/src/plugins/source-pages.js b/packages/saber/src/plugins/source-pages.js
index af1bfeb55..844c2d249 100644
--- a/packages/saber/src/plugins/source-pages.js
+++ b/packages/saber/src/plugins/source-pages.js
@@ -29,6 +29,7 @@ exports.apply = api => {
.map(async file => {
file.relative = file.path
file.absolute = path.join(pagesDir, file.relative)
+ // eslint-disable-next-line require-atomic-updates
file.content = await fs.readFile(file.absolute, 'utf8')
log.verbose(`Found page`, colors.dim(file.absolute))
return file
@@ -81,6 +82,7 @@ exports.apply = api => {
log.verbose(`Emitting page ${outPath}`)
await fs.outputFile(outPath, newContentHash, 'utf8')
+ // eslint-disable-next-line require-atomic-updates
page.internal.saved = true
})
)
@@ -126,9 +128,15 @@ exports.apply = api => {
await api.hooks.emitRoutes.promise()
}
- watcher.on('add', handler('add'))
- watcher.on('unlink', handler('remove'))
- watcher.on('change', handler('change'))
+ watcher.on('add', () => {
+ handler('add')
+ })
+ watcher.on('unlink', () => {
+ handler('remove')
+ })
+ watcher.on('change', () => {
+ handler('change')
+ })
}
})
}
diff --git a/packages/saber/src/plugins/transformer-markdown.js b/packages/saber/src/plugins/transformer-markdown.js
index 624301fa1..4499898a2 100644
--- a/packages/saber/src/plugins/transformer-markdown.js
+++ b/packages/saber/src/plugins/transformer-markdown.js
@@ -1,32 +1,6 @@
const ConfigChain = require('../config-chain')
const resolvePackage = require('../utils/resolvePackage')
-exports.name = 'builtin:transformer-markdown'
-
-exports.apply = api => {
- api.transformers.add('markdown', {
- extensions: ['md'],
- transform(page) {
- const { frontmatter, body } = require('../utils/parseFrontmatter')(
- page.content,
- page.internal.absolute
- )
- Object.assign(page, frontmatter)
- page.content = body
- page.content = renderMarkdown(api, page)
- },
- getPageComponent(page) {
- return `
-
-
- ${page.content || ''}
-
-
- `
- }
- })
-}
-
function renderMarkdown(api, page) {
const { configDir } = api
const { markdown = {} } = api.config
@@ -113,3 +87,29 @@ function renderMarkdown(api, page) {
return md.render(page.content, env)
}
+
+exports.name = 'builtin:transformer-markdown'
+
+exports.apply = api => {
+ api.transformers.add('markdown', {
+ extensions: ['md'],
+ transform(page) {
+ const { frontmatter, body } = require('../utils/parseFrontmatter')(
+ page.content,
+ page.internal.absolute
+ )
+ Object.assign(page, frontmatter)
+ page.content = body
+ page.content = renderMarkdown(api, page)
+ },
+ getPageComponent(page) {
+ return `
+
+
+ ${page.content || ''}
+
+
+ `
+ }
+ })
+}
diff --git a/packages/saber/src/utils/assetsAttribute.ts b/packages/saber/src/utils/assetsAttribute.ts
index f12c076ec..0652a6950 100644
--- a/packages/saber/src/utils/assetsAttribute.ts
+++ b/packages/saber/src/utils/assetsAttribute.ts
@@ -6,7 +6,7 @@ import { slash, isAbsoluteUrl } from 'saber-utils'
* When it's an absolute url or starting with `/`
* `/path` is used to reference files in static folder
*/
-const isExternal = (str: string) => isAbsoluteUrl(str) || /^\//.test(str)
+const isExternal = (str: string) => isAbsoluteUrl(str) || str.startsWith('/')
const MARK = '@@!!SABER_ASSET_MARK_e5968b9a!!@@'
@@ -42,7 +42,4 @@ const requireAssets = (str: string) =>
return `require("${p1}")`
})
-export {
- prefixAssets,
- requireAssets
-}
+export { prefixAssets, requireAssets }
diff --git a/packages/saber/src/utils/getPermalink.js b/packages/saber/src/utils/getPermalink.js
index 41ee8ca88..4725df4a3 100644
--- a/packages/saber/src/utils/getPermalink.js
+++ b/packages/saber/src/utils/getPermalink.js
@@ -1,5 +1,13 @@
// @ts-check
+/**
+ * Left-pad '0' to number that is smaller than 10
+ * @param {number} input
+ */
+function padZero(input) {
+ return input < 10 ? `0${input}` : input
+}
+
// Default:
// about.md => /about.html
// about/index.md => /about
@@ -54,11 +62,3 @@ module.exports = (localeNames, page, permalinks) => {
.replace(/^\/index(\.html)?$/, '/')
.replace(/\/index(\.html)?$/, '')
}
-
-/**
- * Left-pad '0' to number that is smaller than 10
- * @param {number} input
- */
-function padZero(input) {
- return input < 10 ? `0${input}` : input
-}
diff --git a/packages/saber/src/utils/serveDir.js b/packages/saber/src/utils/serveDir.js
index f6e9bcde7..60761cf42 100644
--- a/packages/saber/src/utils/serveDir.js
+++ b/packages/saber/src/utils/serveDir.js
@@ -7,7 +7,7 @@ const serveStatic = require('serve-static')
module.exports = function({ dir, host, port } = {}) {
const server = polka()
server.use(serveStatic(dir))
- server.use(async (req, res, next) => {
+ server.use((req, res, next) => {
if (req.method !== 'GET') return next()
createReadStream(path.join(dir, '404.html')).pipe(res)
})
diff --git a/packages/saber/src/vue-renderer/index.js b/packages/saber/src/vue-renderer/index.js
index e5e8a039e..f8e84a470 100644
--- a/packages/saber/src/vue-renderer/index.js
+++ b/packages/saber/src/vue-renderer/index.js
@@ -5,10 +5,27 @@ const { SyncWaterfallHook } = require('tapable')
const { readJSON } = require('./utils')
const renderHTML = require('./render-html')
+function runCompiler(compiler) {
+ return new Promise((resolve, reject) => {
+ compiler.run((err, stats) => {
+ if (err) return reject(err)
+ resolve(stats)
+ })
+ })
+}
+
function resolveVueApp(...args) {
return path.join(__dirname, '../../vue-app', ...args)
}
+function removeTrailingSlash(input) {
+ if (input === '/') {
+ return input
+ }
+
+ return input.replace(/\/$/, '')
+}
+
const ID = 'vue-renderer'
export class VueRenderer {
@@ -476,6 +493,7 @@ export class VueRenderer {
})
server.get('/_saber/visit-page', async (req, res) => {
+ // eslint-disable-next-line
let [, pathname, hash] = /^([^#]+)(#.+)?$/.exec(req.query.route) || []
pathname = removeTrailingSlash(pathname)
const fullPath = pathname + (hash || '')
@@ -565,20 +583,3 @@ export class VueRenderer {
return server.handler
}
}
-
-function runCompiler(compiler) {
- return new Promise((resolve, reject) => {
- compiler.run((err, stats) => {
- if (err) return reject(err)
- resolve(stats)
- })
- })
-}
-
-function removeTrailingSlash(input) {
- if (input === '/') {
- return input
- }
-
- return input.replace(/\/$/, '')
-}