UNPKG

@oeyoews/tiddlywiki-plugin-dev

Version:

[![](https://img.shields.io/badge/Join-TiddlyWiki_CN-blue)](https://github.com/tiddly-gittly)

223 lines (210 loc) 7.12 kB
import fs from 'fs'; import path from 'path'; import { tmpdir } from 'os'; import { ITiddlerFields } from 'tw5-typed'; import { buildLibrary } from './build'; import { rebuild } from './packup'; import { tiddlywiki, mkdirsForFileSync, waitForFile } from './utils'; /** 项目路径 */ const bypassTiddlers = new Set([ '$:/core', '$:/UpgradeLibrary', '$:/UpgradeLibrary/List', ]); const headerMetadataTiddler: ITiddlerFields = { title: '$:/Modern.TiddlyDev/no-cache-html', tags: ['$:/tags/RawMarkupWikified/TopHead'], text: [ '`', '<meta http-equiv="cache-control" content="no-cache">', '<meta http-equiv="expires" content="0">', '<meta http-equiv="pragma" content="no-cache">', '`', ].join('\n'), } as any; /** * 构建在线HTML版本:核心JS和资源文件不包括在HTML中, 下载后不能使用 * @param {string} [wikiPath='wiki'] wiki 路径 * @param {string} [dist='dist'] 构建产物路径 * @param {string} [htmlName='index.html'] 构建产生的 index 文件名 * @param {string} [excludeFilter='-[is[draft]]'] 要排除的tiddler的过滤表达式,默认为'-[is[draft]]' * @param {boolean} [library=true] 是否同时构建插件库 * @param {string} [srcPath='src'] 插件工程根路径 * @param {string} [excludePlugin] 排除构建的插件 */ export const publishOnlineHTML = async ( wikiPath = 'wiki', dist = 'dist', htmlName = 'index.html', excludeFilter = '-[is[draft]]', library = true, srcPath = 'src', excludePlugin?: string, ) => { // 构建插件库,导出插件 const wikiFolder = path.resolve(wikiPath); const distDir = path.resolve(dist); // 读取、导出外置资源、处理 tiddler const $tw = tiddlywiki([], wikiFolder); const tiddlers: Record<string, ITiddlerFields> = {}; const savePromises: Promise<NodeJS.ErrnoException | null>[] = []; mkdirsForFileSync(path.resolve(distDir, 'media', '1')); $tw.wiki.each(({ fields }, title: string) => { if ( bypassTiddlers.has(title) || title.startsWith('$:/boot/') || title.startsWith('$:/temp/') ) { return; } if ($tw.wiki.isBinaryTiddler(title) || $tw.wiki.isImageTiddler(title)) { const { extension, encoding } = $tw.config.contentTypeInfo[ fields.type || 'text/vnd.tiddlywiki' ] ?? { extension: '.bin', encoding: 'base64' }; const fileName = encodeURIComponent( title.endsWith(extension) ? title : `${title}${extension}`, ); savePromises.push( new Promise(resolve => fs.writeFile( path.resolve(distDir, 'media', fileName), fields.text, encoding as any, resolve, ), ), ); tiddlers[title] = { ...fields, text: '', _canonical_uri: `./media/${encodeURIComponent(fileName)}`, }; } else { tiddlers[title] = { ...fields }; } }); // 将构建好的插件注入 Object.entries( library ? await buildLibrary( path.join(dist, 'library'), excludePlugin, srcPath, wikiPath, ) : await rebuild(tiddlywiki(), srcPath, undefined, false, excludeFilter), ).forEach(([title, tiddler]) => (tiddlers[title] = tiddler)); // 缓存策略 tiddlers[headerMetadataTiddler.title] = headerMetadataTiddler; // 构建 const tmpFolder = fs.mkdtempSync(path.resolve(tmpdir(), 'tiddlywiki-')); try { fs.cpSync( path.resolve(wikiFolder, 'tiddlywiki.info'), path.resolve(tmpFolder, 'tiddlywiki.info'), ); tiddlywiki(Object.values(tiddlers), tmpFolder, [ ...['--output', distDir] /* 指定输出路径 */, ...[ '--rendertiddler', '$:/core/save/offline-external-js', htmlName, 'text/plain', '', 'publishFilter', excludeFilter, ] /* 导出无核心的HTML文件 */, ...[ '--rendertiddler', '$:/core/templates/tiddlywiki5.js', `tiddlywikicore-${$tw.version}.js`, 'text/plain', ] /* 导出核心 */, ]); await waitForFile( path.resolve(distDir, `tiddlywikicore-${$tw.version}.js`), ); } catch (e) { console.error(e); } fs.rmSync(tmpFolder, { recursive: true, force: true }); (await Promise.all(savePromises)).forEach(error => { if (error) { console.error(error); } }); }; /** * 构建离线HTML版本:核心JS和资源文件包括在HTML中, 下载后可以使用(就是单文件版本的wiki) * @param {string} [wikiPath='wiki'] wiki 路径 * @param {string} [dist='dist'] 构建产物路径 * @param {string} [htmlName='index.html'] 构建产生的 index 文件名 * @param {string} [excludeFilter='-[is[draft]]'] 要排除的tiddler的过滤表达式,默认为'-[is[draft]]' * @param {boolean} [library=true] 是否同时构建插件库 * @param {string} [srcPath='src'] 插件工程根路径 * @param {string} [excludePlugin] 排除构建的插件 */ export const publishOfflineHTML = async ( wikiPath = 'wiki', dist = 'dist', htmlName = 'index.html', excludeFilter = '-[is[draft]]', library = true, srcPath = 'src', excludePlugin?: string, ) => { // 构建插件库,导出插件 const distDir = path.resolve(dist); const wikiFolder = path.resolve(wikiPath); // 读取所有 tiddler const $tw = tiddlywiki([], wikiFolder); const tiddlers: Record<string, ITiddlerFields> = {}; $tw.wiki.each(({ fields }, title: string) => { if ( bypassTiddlers.has(title) || title.startsWith('$:/boot/') || title.startsWith('$:/temp/') ) { return; } tiddlers[title] = { ...fields }; }); // 将构建好的插件注入 Object.entries( library ? await buildLibrary( path.join(dist, 'library'), excludePlugin, srcPath, wikiPath, ) : await rebuild(tiddlywiki(), srcPath, undefined, false, excludeFilter), ).forEach(([title, tiddler]) => (tiddlers[title] = tiddler)); // 缓存策略 tiddlers[headerMetadataTiddler.title] = headerMetadataTiddler; // 构建 const tmpFolder = fs.mkdtempSync(path.resolve(tmpdir(), 'tiddlywiki-')); try { fs.cpSync( path.resolve(wikiFolder, 'tiddlywiki.info'), path.resolve(tmpFolder, 'tiddlywiki.info'), ); tiddlywiki(Object.values(tiddlers), tmpFolder, [ ...['--output', distDir] /* 指定输出路径 */, ...[ '--rendertiddler', '$:/plugins/tiddlywiki/tiddlyweb/save/offline', htmlName, 'text/plain', '', 'publishFilter', excludeFilter, ] /* 将wiki导出为HTML */, ]); // 由于导出是异步的,因此等待完成 await waitForFile(path.join(distDir, htmlName)); } catch (e) { console.error(e); } fs.rmSync(tmpFolder, { recursive: true, force: true }); };