Comparing version 1.9.0 to 1.10.0
@@ -44,2 +44,10 @@ "use strict"; | ||
/** | ||
* @description Unregister a decorate function. | ||
* @param {String} layout | ||
*/ | ||
unregister(layout) { | ||
delete this._[layout]; | ||
} | ||
/** | ||
* @description Decorate input file with layout. | ||
@@ -46,0 +54,0 @@ * @param {File} file |
@@ -13,5 +13,5 @@ "use strict"; | ||
const {marked} = require("marked"); | ||
const stylus = require("stylus"); | ||
const Logger = require("./logger"); | ||
const Watcher = require("./watcher"); | ||
const Renderer = require("./renderer"); | ||
@@ -33,4 +33,2 @@ const Compiler = require("./compiler"); | ||
paginateCategories, | ||
getPathFn, | ||
getURLFn, | ||
genCategories, | ||
@@ -77,2 +75,3 @@ genTags, | ||
this.logger.debug("Hikaru is starting..."); | ||
this.watcher = null; | ||
this.types = types; | ||
@@ -220,2 +219,4 @@ this.utils = utils; | ||
this.loadSite(siteDir); | ||
const rawFileDependencies = this.loadFileDependencies(); | ||
this.watcher = new Watcher(this.logger, rawFileDependencies); | ||
try { | ||
@@ -237,3 +238,4 @@ // Modules must be loaded before others. | ||
this.translator, | ||
this.site | ||
this.site, | ||
this.watcher | ||
); | ||
@@ -353,2 +355,31 @@ await this.router.serve(ip, port); | ||
* @private | ||
* @description Read file dependency tree. | ||
* @return {Object} | ||
*/ | ||
loadFileDependencies() { | ||
let rawFileDependencies; | ||
try { | ||
rawFileDependencies = YAML.parse( | ||
fse.readFileSync( | ||
path.join( | ||
this.site["siteConfig"]["themeDir"], | ||
"file-dependencies.yaml" | ||
), | ||
"utf8" | ||
) | ||
); | ||
} catch (error) { | ||
// Should work if theme author does not provide such a file. | ||
rawFileDependencies = {}; | ||
} | ||
const fullRawFileDependencies = {}; | ||
for (const dir in rawFileDependencies) { | ||
const srcDir = path.join(this.site["siteConfig"]["themeDir"], dir); | ||
fullRawFileDependencies[srcDir] = rawFileDependencies[dir]; | ||
} | ||
return fullRawFileDependencies; | ||
} | ||
/** | ||
* @private | ||
* @description Load info about the site. | ||
@@ -424,3 +455,4 @@ * @param {String} siteDir Working site dir. | ||
"opts": this.opts, | ||
"site": this.site | ||
"site": this.site, | ||
"watcher": this.watcher | ||
}); | ||
@@ -436,3 +468,4 @@ }); | ||
async loadScripts() { | ||
const scripts = (await matchFiles(path.join("**", "*.js"), { | ||
// Globs must not contain windows spearators. | ||
const scripts = (await matchFiles("**/*.js", { | ||
"nodir": true, | ||
@@ -442,3 +475,3 @@ "cwd": path.join(this.site["siteDir"], "scripts") | ||
return path.join(this.site["siteDir"], "scripts", filename); | ||
}).concat((await matchFiles(path.join("**", "*.js"), { | ||
}).concat((await matchFiles("**/*.js", { | ||
"nodir": true, | ||
@@ -467,3 +500,4 @@ "cwd": path.join(this.site["siteConfig"]["themeDir"], "scripts") | ||
"opts": this.opts, | ||
"site": this.site | ||
"site": this.site, | ||
"watcher": this.watcher | ||
}); | ||
@@ -513,8 +547,5 @@ }); | ||
} | ||
for (const filename of filenames) { | ||
const lang = path.basename(filename, ext); | ||
const filepath = path.join( | ||
this.site["siteConfig"]["themeLangDir"], | ||
filename | ||
); | ||
const load = async (srcDir, srcPath) => { | ||
const lang = path.basename(srcPath, ext); | ||
const filepath = path.join(srcDir, srcPath); | ||
this.logger.debug(`Hikaru is loading language \`${ | ||
@@ -525,3 +556,29 @@ this.logger.blue(lang) | ||
this.translator.register(lang, language); | ||
}; | ||
for (const filename of filenames) { | ||
load(this.site["siteConfig"]["themeLangDir"], filename); | ||
} | ||
if (this.watcher != null) { | ||
const onAddedOrChanged = async (srcDir, srcPath) => { | ||
// We only register top level yaml files as language. | ||
if (path.dirname(srcPath) !== "." && path.extname(srcPath) !== ext) { | ||
return; | ||
} | ||
load(srcDir, srcPath); | ||
}; | ||
this.watcher.register( | ||
this.site["siteConfig"]["themeLangDir"], | ||
onAddedOrChanged, | ||
onAddedOrChanged, | ||
(srcPath, srcDir) => { | ||
// We only register top level yaml files as language. | ||
if (path.dirname(srcPath) !== "." && path.extname(srcPath) !== ext) { | ||
return; | ||
} | ||
const lang = path.basename(srcPath, ext); | ||
this.translator.unregister(lang); | ||
}, | ||
{"recursive": false} | ||
); | ||
} | ||
} | ||
@@ -538,15 +595,40 @@ | ||
}); | ||
for (const filename of filenames) { | ||
const ext = path.extname(filename); | ||
const layout = path.basename(filename, ext); | ||
const load = async (srcDir, srcPath) => { | ||
const ext = path.extname(srcPath); | ||
const layout = path.basename(srcPath, ext); | ||
this.logger.debug(`Hikaru is loading layout \`${ | ||
this.logger.blue(layout) | ||
}\`...`); | ||
const filepath = path.join( | ||
this.site["siteConfig"]["themeLayoutDir"], | ||
filename | ||
); | ||
const filepath = path.join(srcDir, srcPath); | ||
const fn = await this.compiler.compile(filepath); | ||
this.decorator.register(layout, fn); | ||
}; | ||
for (const filename of filenames) { | ||
load(this.site["siteConfig"]["themeLayoutDir"], filename); | ||
} | ||
if (this.watcher != null) { | ||
const onAddedOrChanged = (srcDir, srcPath) => { | ||
// We only register top level template files as layout. | ||
if (path.dirname(srcPath) !== ".") { | ||
return; | ||
} | ||
load(srcDir, srcPath); | ||
}; | ||
this.watcher.register( | ||
this.site["siteConfig"]["themeLayoutDir"], | ||
onAddedOrChanged, | ||
onAddedOrChanged, | ||
(srcDir, srcPath) => { | ||
// We only register top level template files as layout. | ||
if (path.dirname(srcPath) !== ".") { | ||
return; | ||
} | ||
const ext = path.extname(srcPath); | ||
const layout = path.basename(srcPath, ext); | ||
this.decorator.unregister(layout); | ||
} | ||
// Even though we don't load files recursively, we need to watch files | ||
// recursively, because our loaded layouts may depend on them. | ||
); | ||
} | ||
} | ||
@@ -572,59 +654,2 @@ | ||
}); | ||
const stylConfig = this.site["siteConfig"]["stylus"] || {}; | ||
const getPath = getPathFn(this.site["siteConfig"]["rootDir"]); | ||
const getURL = getURLFn( | ||
this.site["siteConfig"]["baseURL"], | ||
this.site["siteConfig"]["rootDir"] | ||
); | ||
this.renderer.register(".styl", ".css", (file) => { | ||
return new Promise((resolve, reject) => { | ||
stylus(file["text"]).use((style) => { | ||
style.define("getSiteConfig", (data) => { | ||
const keys = data["val"].toString().trim().split("."); | ||
let res = this.site["siteConfig"]; | ||
for (const k of keys) { | ||
if (res[k] == null) { | ||
return null; | ||
} | ||
res = res[k]; | ||
} | ||
return res; | ||
}); | ||
style.define("getThemeConfig", (data) => { | ||
const keys = data["val"].toString().trim().split("."); | ||
let res = this.site["themeConfig"]; | ||
for (const k of keys) { | ||
if (res[k] == null) { | ||
return null; | ||
} | ||
res = res[k]; | ||
} | ||
return res; | ||
}); | ||
style.define("getPath", (data) => { | ||
return getPath(data["val"].toString().trim()); | ||
}); | ||
style.define("getURL", (data) => { | ||
return getURL(data["val"].toString().trim()); | ||
}); | ||
style.define("siteConfig", this.site["siteConfig"]); | ||
style.define("themeConfig", this.site["themeConfig"]); | ||
style.define("srcDir", file["srcDir"]); | ||
style.define("srcPath", file["srcPath"]); | ||
style.define("docDir", file["docDir"]); | ||
style.define("docPath", file["docPath"]); | ||
}).set("filename", path.join( | ||
file["srcDir"], file["srcPath"] | ||
)).set("sourcemap", stylConfig["sourcemap"]) | ||
.set("compress", stylConfig["compress"]) | ||
.set("include css", true).render((error, result) => { | ||
if (error != null) { | ||
return reject(error); | ||
} | ||
file["content"] = result; | ||
return resolve(file); | ||
}); | ||
}); | ||
}); | ||
} | ||
@@ -631,0 +656,0 @@ |
@@ -8,5 +8,3 @@ "use strict"; | ||
const fse = require("fs-extra"); | ||
const path = require("path"); | ||
const http = require("http"); | ||
const chokidar = require("chokidar"); | ||
const {Site, File} = require("./types"); | ||
@@ -45,6 +43,14 @@ const { | ||
* @param {Site} site | ||
* @param {Watcher} [watcher] | ||
* @return {Router} | ||
*/ | ||
constructor( | ||
logger, renderer, processor, generator, decorator, translator, site | ||
logger, | ||
renderer, | ||
processor, | ||
generator, | ||
decorator, | ||
translator, | ||
site, | ||
watcher = null | ||
) { | ||
@@ -63,6 +69,3 @@ this.logger = logger; | ||
this.listening = false; | ||
this.watchers = []; | ||
this.watchedEvents = []; | ||
this.sourcePages = []; | ||
this.handling = false; | ||
this.watcher = watcher; | ||
this.getURL = getURLFn( | ||
@@ -182,3 +185,4 @@ this.site["siteConfig"]["baseURL"], this.site["siteConfig"]["rootDir"] | ||
async matchAll() { | ||
return (await matchFiles(path.join("**", "*"), { | ||
// Globs must not contain windows spearators. | ||
return (await matchFiles("**/*", { | ||
"nodir": true, | ||
@@ -193,3 +197,3 @@ "dot": false, | ||
); | ||
}).concat((await matchFiles(path.join("**", "*"), { | ||
}).concat((await matchFiles("**/*", { | ||
"nodir": true, | ||
@@ -226,33 +230,43 @@ "dot": true, | ||
watchAll() { | ||
for (const srcDir of [ | ||
this.site["siteConfig"]["themeSrcDir"], | ||
this.site["siteConfig"]["srcDir"] | ||
]) { | ||
const watcher = chokidar.watch(path.join("**", "*"), { | ||
"cwd": srcDir, "ignoreInitial": true | ||
}); | ||
this.watchers.push(watcher); | ||
for (const event of ["add", "change", "unlink"]) { | ||
watcher.on(event, (srcPath) => { | ||
this.logger.debug( | ||
`Hikaru is watching event \`${ | ||
this.logger.blue(event) | ||
}\` from \`${ | ||
this.logger.cyan(path.join(srcDir, srcPath)) | ||
}\`...` | ||
); | ||
const i = this.watchedEvents.findIndex((p) => { | ||
return p["srcDir"] === srcDir && p["srcPath"] === srcPath; | ||
}); | ||
if (i !== -1) { | ||
// Just update event. | ||
this.watchedEvents[i]["type"] = event; | ||
} else { | ||
// Not found. | ||
this.watchedEvents.push({event, srcDir, srcPath}); | ||
} | ||
setImmediate(this.handleEvents.bind(this)); | ||
}); | ||
if (this.watcher == null) { | ||
return; | ||
} | ||
const onAddedOrChanged = async (srcDir, srcPath) => { | ||
const newFile = new File( | ||
this.site["siteConfig"]["docDir"], srcDir, srcPath | ||
); | ||
await this.loadFile(newFile); | ||
// TODO: put files in a queue and flush queue only once. | ||
await this.handle(); | ||
this.buildServerRoutes( | ||
this.site["assets"] | ||
.concat(this.site["posts"]) | ||
.concat(this.site["pages"]) | ||
.concat(this.site["files"]) | ||
); | ||
}; | ||
this.watcher.register( | ||
[ | ||
this.site["siteConfig"]["themeSrcDir"], | ||
this.site["siteConfig"]["srcDir"] | ||
], | ||
onAddedOrChanged, | ||
onAddedOrChanged, | ||
async (srcDir, srcPath) => { | ||
const file = new File( | ||
this.site["siteConfig"]["docDir"], srcDir, srcPath | ||
); | ||
// TODO: put files in a queue and flush queue only once. | ||
for (const key of Site.arrayKeys) { | ||
delSite(this.site, key, file); | ||
} | ||
await this.handle(); | ||
this.buildServerRoutes( | ||
this.site["assets"] | ||
.concat(this.site["posts"]) | ||
.concat(this.site["pages"]) | ||
.concat(this.site["files"]) | ||
); | ||
} | ||
} | ||
); | ||
} | ||
@@ -265,39 +279,9 @@ | ||
unwatchAll() { | ||
let w; | ||
while ((w = this.watchers.shift()) != null) { | ||
w.close(); | ||
} | ||
} | ||
/** | ||
* @private | ||
* @description Handle watcher events. | ||
*/ | ||
async handleEvents() { | ||
// Keep handling atomic. Prevent repeatedly handling. | ||
if (this.watchedEvents.length === 0 || this.handling) { | ||
if (this.watcher == null) { | ||
return; | ||
} | ||
this.handling = true; | ||
let e; | ||
while ((e = this.watchedEvents.shift()) != null) { | ||
const file = new File( | ||
this.site["siteConfig"]["docDir"], e["srcDir"], e["srcPath"] | ||
); | ||
if (e["event"] === "unlink") { | ||
for (const key of Site.arrayKeys) { | ||
delSite(this.site, key, file); | ||
} | ||
} else { | ||
await this.loadFile(file); | ||
} | ||
} | ||
await this.handle(); | ||
this.buildServerRoutes( | ||
this.site["assets"] | ||
.concat(this.site["posts"]) | ||
.concat(this.site["pages"]) | ||
.concat(this.site["files"]) | ||
); | ||
this.handling = false; | ||
this.watcher.unregister([ | ||
this.site["siteConfig"]["themeSrcDir"], | ||
this.site["siteConfig"]["srcDir"] | ||
]); | ||
} | ||
@@ -304,0 +288,0 @@ |
@@ -45,2 +45,16 @@ "use strict"; | ||
/** | ||
* @description Unregister a kind of language. | ||
* @param {(String|String[])} lang Language names. | ||
*/ | ||
unregister(lang) { | ||
if (isArray(lang)) { | ||
for (const l of lang) { | ||
delete this._[l]; | ||
} | ||
} else { | ||
delete this._[lang]; | ||
} | ||
} | ||
/** | ||
* @description List registered language. | ||
@@ -47,0 +61,0 @@ * @return {String[]} |
{ | ||
"name": "hikarujs", | ||
"version": "1.9.0", | ||
"version": "1.10.0", | ||
"description": "A static site generator that generates routes based on directories naturally.", | ||
@@ -40,3 +40,2 @@ "main": "hikaru/index.js", | ||
"parse5": "^6.0.1", | ||
"stylus": "^0.56.0", | ||
"yaml": "^1.10.2" | ||
@@ -43,0 +42,0 @@ }, |
Sorry, the diff of this file is not supported yet
114308
10
20
3286
- Removedstylus@^0.56.0
- Removedatob@2.1.2(transitive)
- Removedcss@3.0.0(transitive)
- Removeddebug@4.4.0(transitive)
- Removeddecode-uri-component@0.2.2(transitive)
- Removedms@2.1.3(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedsax@1.2.4(transitive)
- Removedsource-map@0.6.10.7.4(transitive)
- Removedsource-map-resolve@0.6.0(transitive)
- Removedstylus@0.56.0(transitive)