UNPKG

tiddlywiki-plugin-dev

Version:

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

351 lines (350 loc) 11.7 kB
import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; import chalk from "chalk"; import sha256 from "sha256"; import esbuild from "esbuild"; import UglifyJS from "uglify-js"; import CleanCSS from "clean-css"; import cliProgress from "cli-progress"; import browserslist from "browserslist"; import postCssPlugin from "esbuild-style-plugin"; import tailwindcssPostcss from "@tailwindcss/postcss"; import autoprefixer from "autoprefixer"; import esbuildSvelte from "esbuild-svelte"; import sveltePreprocess from "svelte-preprocess"; import { esbuildPluginBrowserslist } from "esbuild-plugin-browserslist"; import { walkFilesSync } from "./utils.js"; const nodejsBuiltinModules = [ "assert", "buffer", "child_process", "cluster", "crypto", "dgram", "dns", "domain", "events", "fs", "fsevents", "http", "https", "net", "os", "path", "punycode", "querystring", "readline", "stream", "string_decoder", "timers", "tls", "tty", "url", "util", "v8", "vm", "zlib" ]; const moduleDir = path.dirname(fileURLToPath(import.meta.url)); const injectPath = path.resolve(moduleDir, "esbuild-inject.js"); const rootPath = process.cwd(); const pluginCache = {}; const UglifyJSOption = { warnings: false, v8: true, ie: true, webkit: true }; const cleanCSS = new CleanCSS({ compatibility: "ie9", level: 2 }); const minifyTiddler = (tiddler) => { const { text, type } = tiddler; try { if (type === "application/javascript") { const minified = UglifyJS.minify(text, UglifyJSOption).code; if (minified !== void 0) { return { ...tiddler, text: minified }; } } else if (type === "text/css") { const minified = cleanCSS.minify(text).styles; if (minified !== void 0) { return { ...tiddler, text: minified }; } } } catch (e) { console.error(e); console.error(`Failed to minify ${tiddler.title}.`); } return tiddler; }; const rebuild = async ($tw, pluginsDir, updatePaths = [], devMode = true, excludeFilter) => { const baseDir = path.resolve(pluginsDir); if (!fs.existsSync(baseDir)) { return []; } const tailwindConfigPath = path.resolve(".", "tailwind.config.js"); if (!fs.existsSync(tailwindConfigPath)) { fs.writeFileSync( tailwindConfigPath, [ "module.exports = {", " content: ['./src/**/*.{mjs,cjs,js,ts,jsx,tsx}'],", " theme: { extend: {} },", " plugins: [],", "};" ].join("\n"), "utf-8" ); } console.log(chalk.green.bold("Compiling...")); const bar = new cliProgress.SingleBar( { format: `${chalk.green("{bar}")} {percentage}% | {plugin}`, stopOnComplete: true }, cliProgress.Presets.shades_classic ); const updateDirs = Array.from( new Set( updatePaths.filter((file) => file).map((file) => path.resolve(path.dirname(file))) ) ); const pluginDirs = fs.readdirSync(baseDir).map((dirname) => path.resolve(baseDir, dirname)).filter((dir) => fs.statSync(dir).isDirectory()); bar.start(pluginDirs.length, 0); const plugins = await Promise.all( pluginDirs.map(async (dir, index) => { bar.update(index, { plugin: path.basename(dir) }); const update = !Object.prototype.hasOwnProperty.call(pluginCache, dir) || updateDirs.length === 0 || updateDirs.some((updateDir) => updateDir.startsWith(dir)); if (!update) { bar.update(index + 1, { plugin: path.basename(dir) }); return pluginCache[dir]; } if (!fs.existsSync(path.resolve(dir, "plugin.info"))) { return void 0; } const plugin = $tw.loadPluginFolder(dir); if (!plugin?.title) { return void 0; } if (excludeFilter && $tw.wiki.filterTiddlers(`[[${plugin.title}]] +${excludeFilter}`).length > 0) { return void 0; } const browserslistStr = plugin["Modern.TiddlyDev#BrowsersList"] ?? ">0.25%, not ie 11, not op_mini all"; const externalModules = $tw.utils.parseStringArray( plugin["Modern.TiddlyDev#ExternalModules"] ?? "" ); const nodeBuildInModulesToNotExternal = $tw.utils.parseStringArray( plugin["Modern.TiddlyDev#NodeModulesNotExternal"] ?? "" ); const sourceMap = plugin["Modern.TiddlyDev#SourceMap"]?.toLowerCase?.() === "true"; const minifyPlugin = plugin["Modern.TiddlyDev#Minify"]?.toLowerCase?.() !== "false"; const tiddlers = JSON.parse(plugin.text).tiddlers; $tw.wiki.deleteTiddler(plugin.title); Object.keys(tiddlers).forEach((title) => { if (fs.existsSync(title) && !fs.existsSync(`${title}.meta`)) { delete tiddlers[title]; } }); const entryPoints = []; const metaMap = /* @__PURE__ */ new Map(); walkFilesSync(dir, (filepath) => { let meta = $tw.loadMetadataForFile(filepath); if (!meta) { return; } metaMap.set(filepath, meta); if ([".ts", ".tsx", ".cjs", ".mjs", ".jsx"].includes( path.extname(filepath).toLowerCase() )) { if (meta["Modern.TiddlyDev#IncludeSource"] === "true") { tiddlers[meta.title] = { ...meta, text: fs.readFileSync(filepath, "utf-8"), "module-type": void 0 }; if (meta["Modern.TiddlyDev#NoCompile"] !== "true") { entryPoints.push(filepath); const titlePath = meta.title.split("/"); const parts = titlePath[titlePath.length - 1].split("."); if (parts.length < 2 || parts[parts.length - 1].toLowerCase() === "js" || !["ts", "tsx", "cjs", "mjs", "jsx"].includes( parts[parts.length - 1].toLowerCase() )) { parts.push("js"); } else { parts[parts.length - 1] = "js"; } titlePath[titlePath.length - 1] = parts.join("."); meta = { ...meta, title: titlePath.join("/") }; } else { } } else { delete tiddlers[meta.title]; if (meta["Modern.TiddlyDev#NoCompile"] !== "true") { entryPoints.push(filepath); const titlePath = meta.title.split("/"); const parts = titlePath[titlePath.length - 1].split("."); if (parts.length < 2 || !["js", "ts", "tsx", "cjs", "mjs", "jsx"].includes( parts[parts.length - 1].toLowerCase() )) { parts.push("js"); } else { parts[parts.length - 1] = "js"; } titlePath[titlePath.length - 1] = parts.join("."); meta = { ...meta, title: titlePath.join("/") }; } else { } } } metaMap.set(filepath, meta); }); const { outputFiles, metafile } = await esbuild.build({ entryPoints, bundle: true, // 为什么不用 ESbuild 的压缩:UglifyJS 的压缩效率更好 // 参考:https://github.com/privatenumber/minification-benchmarks minify: false, write: false, allowOverwrite: true, // incremental: true, outdir: baseDir, outbase: baseDir, sourcemap: devMode || sourceMap ? "inline" : false, // https://esbuild.github.io/api/#format format: "cjs", // https://esbuild.github.io/api/#tree-shaking treeShaking: true, // https://esbuild.github.io/api/#platform platform: "browser", // https://esbuild.github.io/api/#external external: [ "$:/*", // allow whitelist some node build-in modules ...nodejsBuiltinModules.filter( (name) => !nodeBuildInModulesToNotExternal.includes(name) ), ...externalModules ?? [] ], inject: [injectPath], // https://esbuild.github.io/api/#analyze metafile: true, banner: { js: "/* Compiled by Modern.TiddlyDev: https://github.com/tiddly-gittly/Modern.TiddlyDev */", css: "/* Compiled by Modern.TiddlyDev: https://github.com/tiddly-gittly/Modern.TiddlyDev */" }, loader: { ".png": "dataurl", ".woff": "dataurl", ".woff2": "dataurl", ".eot": "dataurl", ".ttf": "dataurl", ".svg": "dataurl" }, plugins: [ // http://browserl.ist/?q=%3E0.25%25%2C+not+ie+11%2C+not+op_mini+all esbuildPluginBrowserslist(browserslist(browserslistStr), { printUnknownTargets: false }), postCssPlugin({ postcss: { plugins: [tailwindcssPostcss(), autoprefixer] } }), esbuildSvelte({ preprocess: sveltePreprocess() }) ] }); outputFiles.forEach((file) => { const output = metafile.outputs[path.relative(rootPath, file.path).split(path.sep).join("/")]; let meta = {}; if (output.entryPoint) { const resolved = path.resolve(output.entryPoint); const relatived = path.relative(dir, output.entryPoint); if (metaMap.has(resolved)) { meta = { ...metaMap.get(resolved), type: "application/javascript", "Modern.TiddlyDev#Origin": relatived }; } else { return; } } else { const name = Object.keys(output.inputs)[0]; if (name) { const resolved = path.resolve(name); const relatived = path.relative(dir, name); const type = $tw.config.fileExtensionInfo[path.extname(file.path).toLowerCase()]?.type ?? ""; meta = { title: "", tags: type === "text/css" ? ["$:/tags/Stylesheet"] : [], ...metaMap.get(resolved) ?? {}, type, "Modern.TiddlyDev#Origin": relatived }; } if (!meta.title) { const parsed = path.parse(path.relative(dir, file.path)); const tmp = path.join(plugin.title, parsed.dir, parsed.name); if (Object.prototype.hasOwnProperty.call(tiddlers, `${tmp}${parsed.ext}`)) { let id = 1; while (Object.prototype.hasOwnProperty.call(tiddlers, `${tmp}${id}${parsed.ext}`)) { id++; } meta.title = `${tmp}${id}${parsed.ext}`; } else { meta.title = `${tmp}${parsed.ext}`; } } } tiddlers[meta.title] = { ...meta, text: file.text }; }); if (!devMode && minifyPlugin) { Object.keys(tiddlers).forEach((title) => { if (tiddlers[title]["Modern.TiddlyDev#Minify"] !== "false") { tiddlers[title] = minifyTiddler(tiddlers[title]); } }); } const t = { ...plugin, text: JSON.stringify({ tiddlers }) }; pluginCache[dir] = {}; for (const key of Object.keys(t).sort()) { pluginCache[dir][key] = t[key]; } if (!devMode) { pluginCache[dir]["Modern.TiddlyDev#SHA256-Hashed"] = sha256( JSON.stringify(pluginCache[dir]) ); } bar.update(index + 1, { plugin: path.basename(dir) }); return pluginCache[dir]; }) ); console.log(""); return plugins.filter((plugin) => plugin !== void 0); }; export { rebuild };