seemore 1.12.18 → 1.12.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -11
- package/dist/cli/index.js +112 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<img src="https://raw.githubusercontent.com/arifszn/seemore/main/assets/logo.png" alt="seemore" width="280">
|
|
5
|
-
<h4 align="center">Let AI write the Markdown. Let seemore show it better
|
|
5
|
+
<h4 align="center">Zero config documentation tool - Let AI write the Markdown. Let seemore show it better.</h4>
|
|
6
6
|
<p align="center">
|
|
7
7
|
<a href="https://www.npmjs.com/package/seemore">
|
|
8
8
|
<img src="https://img.shields.io/npm/v/seemore"/>
|
|
9
9
|
</a>
|
|
10
|
+
<a href="https://arifszn.github.io/seemore/download">
|
|
11
|
+
<img src="https://img.shields.io/badge/Desktop-macOS%20%C2%B7%20Windows%20%C2%B7%20Linux-111111"/>
|
|
12
|
+
</a>
|
|
10
13
|
<a href="https://open-vsx.org/extension/arifszn/seemore-vscode">
|
|
11
14
|
<img src="https://img.shields.io/badge/Open_VSX-Registry-C160EF?logo=eclipseide&logoColor=white"/>
|
|
12
15
|
</a>
|
|
@@ -34,16 +37,22 @@
|
|
|
34
37
|
|
|
35
38
|
AI tools write Markdown fast. They write specs, notes, guides, READMEs, and whole folders of files. No person can read that much Markdown at that speed.
|
|
36
39
|
|
|
37
|
-
A folder of `.md` files has no order. You cannot click a link between files. You cannot search across them.
|
|
38
|
-
|
|
39
40
|
**seemore** points at that folder and renders it as a real site. Other docs frameworks need a project first: a scaffold, a config file, a `docs/` layout, and a build step in your repo. seemore needs only a folder that already exists. It does not move your files, and it does not need any code. If you stop using seemore, your files stay where they are.
|
|
40
41
|
|
|
41
42
|
<p align="center">
|
|
42
|
-
<
|
|
43
|
+
<video src="https://github.com/user-attachments/assets/10f105c9-924e-4a89-bcba-4d46eefe928d" width="640" controls muted></video>
|
|
43
44
|
</p>
|
|
44
45
|
|
|
45
46
|
## Quick start
|
|
46
47
|
|
|
48
|
+
You can use seemore in two ways.
|
|
49
|
+
|
|
50
|
+
### Desktop app
|
|
51
|
+
|
|
52
|
+
Download the app for macOS, Windows or Linux from the [download page](https://arifszn.github.io/seemore/download). Open a folder or a `.md` file, and it opens as a site.
|
|
53
|
+
|
|
54
|
+
### Command line
|
|
55
|
+
|
|
47
56
|
seemore needs [Node.js](https://nodejs.org) 20 or newer.
|
|
48
57
|
|
|
49
58
|
```bash
|
|
@@ -55,11 +64,11 @@ npx seemore # renders this folder at http://localhost:4040
|
|
|
55
64
|
|
|
56
65
|
The [seemore website](https://arifszn.github.io/seemore) is a folder of Markdown, rendered by seemore. Your own folder will look the same way.
|
|
57
66
|
|
|
58
|
-
<details
|
|
67
|
+
<details>
|
|
59
68
|
<summary><strong>Or ask an AI agent to set it up</strong></summary>
|
|
60
69
|
<br/>
|
|
61
70
|
|
|
62
|
-
If you
|
|
71
|
+
If you work with an AI agent, the [agent skill](https://github.com/arifszn/seemore-skill) does the setup for you.
|
|
63
72
|
|
|
64
73
|
### Claude Code (Plugin Marketplace)
|
|
65
74
|
|
|
@@ -98,10 +107,6 @@ Or call the skill by name:
|
|
|
98
107
|
|
|
99
108
|
</details>
|
|
100
109
|
|
|
101
|
-
<p align="center">
|
|
102
|
-
<video src="https://github.com/user-attachments/assets/dd8280d2-fc8a-47c9-b2db-b1a1ccde81ce" width="640" controls muted></video>
|
|
103
|
-
</p>
|
|
104
|
-
|
|
105
110
|
## What you get
|
|
106
111
|
|
|
107
112
|
Point seemore at any Markdown you already have: AI-written notes, project docs, RFCs, API references, specs, or an engineering handbook. You get:
|
|
@@ -109,6 +114,7 @@ Point seemore at any Markdown you already have: AI-written notes, project docs,
|
|
|
109
114
|
- **Zero config**: no config file, no code, no files to move
|
|
110
115
|
- **Live preview**: files appear, disappear and reorder as you edit them
|
|
111
116
|
- **Edit in place**: double-click any block in the preview to fix its Markdown
|
|
117
|
+
- **Desktop app**: for macOS, Windows and Linux, with an integrated terminal
|
|
112
118
|
- **Editor integration**: one extension covers VS Code, Cursor, Antigravity and other VS Code-compatible editors, including remote workspaces
|
|
113
119
|
- **Documentation framework**: `seemore build` renders the whole site to HTML, ready to deploy on any host
|
|
114
120
|
- **Password protection**: protect the built site with one shared password, with no server required
|
|
@@ -128,6 +134,18 @@ The preview is also an editor. Double-click any paragraph, heading, list item, q
|
|
|
128
134
|
|
|
129
135
|
Inline editing works only for local previews.
|
|
130
136
|
|
|
137
|
+
## Open with the desktop app
|
|
138
|
+
|
|
139
|
+
Download the app from the [download page](https://arifszn.github.io/seemore/download). It opens a folder or a `.md` file as a site in its own window, with an integrated terminal.
|
|
140
|
+
|
|
141
|
+
<p align="center">
|
|
142
|
+
<img src="https://raw.githubusercontent.com/arifszn/seemore/main/packages/site/assets/desktop-app.png" alt="The seemore desktop app in the black theme, showing a folder of research notes as a site: a paragraph's Markdown source open in the inline editor above a flowchart, and the integrated terminal listing the folder's Markdown files" width="640"/>
|
|
143
|
+
</p>
|
|
144
|
+
|
|
145
|
+
- **Open a folder**: use **File > Open Folder**, or drag the folder onto the app.
|
|
146
|
+
- **Open a file**: double-click a `.md` file, or use **File > Open File**.
|
|
147
|
+
- **Use the integrated terminal**: it opens in the site's folder, so you can run an AI agent, git or a script next to the pages it changes.
|
|
148
|
+
|
|
131
149
|
## View in your code editor
|
|
132
150
|
|
|
133
151
|
Install **seemore** from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=arifszn.seemore-vscode) or [Open VSX](https://open-vsx.org/extension/arifszn/seemore-vscode). You get the same rendered site as a panel beside your editor, with no terminal and no browser tab to manage. The extension bundles the CLI, so nothing downloads and nothing goes on your PATH.
|
|
@@ -214,7 +232,7 @@ The [themes page](https://arifszn.github.io/seemore/themes) shows all twelve.
|
|
|
214
232
|
|
|
215
233
|
## Content
|
|
216
234
|
|
|
217
|
-
Works with
|
|
235
|
+
Works with `.md`, `.markdown` and `.mdx`. **[Mermaid](https://mermaid.js.org)** and **[D2](https://d2lang.com)** diagrams render live in the browser, straight from a ` ```mermaid ` or ` ```d2 ` code fence:
|
|
218
236
|
|
|
219
237
|
<p align="center">
|
|
220
238
|
<img src="https://raw.githubusercontent.com/arifszn/seemore/main/packages/site/assets/diagrams.png" alt="A mermaid code fence on the Diagrams page, with the flowchart it produces below: folder of .md into seemore, then a live site and static HTML" width="640"/>
|
package/dist/cli/index.js
CHANGED
|
@@ -201,7 +201,7 @@ function withBase(base, href) {
|
|
|
201
201
|
// src/node/content/slug.ts
|
|
202
202
|
import { slug as slugify } from "github-slugger";
|
|
203
203
|
var INDEX_NAMES = /* @__PURE__ */ new Set(["index", "readme"]);
|
|
204
|
-
var CONTENT_EXT = /\.mdx
|
|
204
|
+
var CONTENT_EXT = /\.(?:mdx?|markdown)$/i;
|
|
205
205
|
function toPosix(file) {
|
|
206
206
|
return file.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
207
207
|
}
|
|
@@ -672,6 +672,14 @@ function toMap(input) {
|
|
|
672
672
|
return Object.fromEntries(set);
|
|
673
673
|
}
|
|
674
674
|
|
|
675
|
+
// src/node/config/names.ts
|
|
676
|
+
var CONFIG_NAMES = [
|
|
677
|
+
"seemore.config.ts",
|
|
678
|
+
"seemore.config.mts",
|
|
679
|
+
"seemore.config.js",
|
|
680
|
+
"seemore.config.mjs"
|
|
681
|
+
];
|
|
682
|
+
|
|
675
683
|
// src/node/config/schema.ts
|
|
676
684
|
import { z } from "zod";
|
|
677
685
|
var THEMES = [
|
|
@@ -756,7 +764,6 @@ var configSchema = z.object({
|
|
|
756
764
|
});
|
|
757
765
|
|
|
758
766
|
// src/node/config/load.ts
|
|
759
|
-
var CONFIG_NAMES = ["seemore.config.ts", "seemore.config.mts", "seemore.config.js", "seemore.config.mjs"];
|
|
760
767
|
function resolveConfig(input, options) {
|
|
761
768
|
const parsed = parseOrThrow(input, options.configFile);
|
|
762
769
|
const search = parsed.search === "static" ? { provider: "static" } : parsed.search;
|
|
@@ -883,7 +890,7 @@ function explain(issue) {
|
|
|
883
890
|
|
|
884
891
|
// src/node/content/links.ts
|
|
885
892
|
import { slug as slugify2 } from "github-slugger";
|
|
886
|
-
var CONTENT_EXT2 = /\.mdx
|
|
893
|
+
var CONTENT_EXT2 = /\.(?:mdx?|markdown)$/i;
|
|
887
894
|
function createLinkResolver(pages, base) {
|
|
888
895
|
const byPath = /* @__PURE__ */ new Map();
|
|
889
896
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -1050,7 +1057,7 @@ var metaSchema = z4.object({
|
|
|
1050
1057
|
function scan(options) {
|
|
1051
1058
|
const contentRoot = resolve3(options.contentRoot);
|
|
1052
1059
|
const find = (patterns, keep) => findFiles(contentRoot, patterns, keep, options.exclude ?? [], options.include ?? []);
|
|
1053
|
-
const contentFiles = find(["**/*.md", "**/*.mdx"], /\.mdx
|
|
1060
|
+
const contentFiles = find(["**/*.md", "**/*.markdown", "**/*.mdx"], /\.(?:mdx?|markdown)$/);
|
|
1054
1061
|
const metaFiles = find(["**/meta.json"], /(?:^|\/)meta\.json$/);
|
|
1055
1062
|
const { routes, errors, warnings } = resolveRoutes(contentFiles);
|
|
1056
1063
|
const pages = [];
|
|
@@ -1121,7 +1128,7 @@ function synthesiseOrderMeta(pages, metaDirs) {
|
|
|
1121
1128
|
for (const [dir, dirPages] of byDir) {
|
|
1122
1129
|
if (metaDirs.has(dir)) continue;
|
|
1123
1130
|
if (!dirPages.some((p) => typeof p.data.order === "number" || p.isIndex)) continue;
|
|
1124
|
-
const ordered = [...dirPages].sort(compareForOrder).map((p) => basename(p.file).replace(/\.mdx
|
|
1131
|
+
const ordered = [...dirPages].sort(compareForOrder).map((p) => basename(p.file).replace(/\.(?:mdx?|markdown)$/i, ""));
|
|
1125
1132
|
out.push({
|
|
1126
1133
|
type: "meta",
|
|
1127
1134
|
path: dir === "." ? "meta.json" : `${dir}/meta.json`,
|
|
@@ -1489,7 +1496,7 @@ function rehypeSeemoreRawHtml() {
|
|
|
1489
1496
|
const transform = rehypeRaw({ passThrough: [...MDX_NODE_TYPES] });
|
|
1490
1497
|
return (tree, file) => {
|
|
1491
1498
|
const path = file.path ?? "";
|
|
1492
|
-
if (!/\.md(?:$|\?)/.test(path)) return tree;
|
|
1499
|
+
if (!/\.(?:md|markdown)(?:$|\?)/.test(path)) return tree;
|
|
1493
1500
|
return transform(tree, file);
|
|
1494
1501
|
};
|
|
1495
1502
|
}
|
|
@@ -1741,7 +1748,7 @@ function createRehypePlugins(options) {
|
|
|
1741
1748
|
}
|
|
1742
1749
|
|
|
1743
1750
|
// src/node/vite/plugin.ts
|
|
1744
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1751
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1745
1752
|
import { createRequire as createRequire2 } from "module";
|
|
1746
1753
|
import { dirname as dirname8 } from "path";
|
|
1747
1754
|
|
|
@@ -1936,6 +1943,28 @@ function seemorePlugin({ ctx, serveSearch = false }) {
|
|
|
1936
1943
|
res.setHeader("Content-Type", "application/json");
|
|
1937
1944
|
res.end(JSON.stringify({ url: withBase(ctx.config.base, page.url) }));
|
|
1938
1945
|
});
|
|
1946
|
+
devServer.middlewares.use((req, res, next) => {
|
|
1947
|
+
const [path = "", query = ""] = (req.url ?? "").split("?");
|
|
1948
|
+
if (path !== "/__seemore/page") return next();
|
|
1949
|
+
const url = new URLSearchParams(query).get("url");
|
|
1950
|
+
if (url === null) return send(res, 400, { error: 'Missing "url" query parameter.' });
|
|
1951
|
+
const route = routeOf(url, ctx.config.base);
|
|
1952
|
+
const page = ctx.pages().find((p) => p.url === route);
|
|
1953
|
+
if (page === void 0) return send(res, 404, { error: `No page is served at ${url}.` });
|
|
1954
|
+
return send(res, 200, { file: page.absPath });
|
|
1955
|
+
});
|
|
1956
|
+
devServer.middlewares.use((req, res, next) => {
|
|
1957
|
+
const path = (req.url ?? "").split("?")[0] ?? "";
|
|
1958
|
+
if (path !== "/__seemore/site") return next();
|
|
1959
|
+
const configFile = ctx.config.configFile;
|
|
1960
|
+
void loadConfig({
|
|
1961
|
+
root: ctx.contentRoot,
|
|
1962
|
+
configPath: configFile !== void 0 && existsSync5(configFile) ? configFile : void 0
|
|
1963
|
+
}).then(
|
|
1964
|
+
({ config }) => send(res, 200, { base: config.base, pageActions: config.pageActions, auth: config.auth !== void 0 }),
|
|
1965
|
+
(error) => send(res, 500, { error: error instanceof Error ? error.message : String(error) })
|
|
1966
|
+
);
|
|
1967
|
+
});
|
|
1939
1968
|
if (ctx.config.features["content.edit"]) {
|
|
1940
1969
|
devServer.middlewares.use((req, res, next) => {
|
|
1941
1970
|
const path = (req.url ?? "").split("?")[0] ?? "";
|
|
@@ -1957,10 +1986,29 @@ function seemorePlugin({ ctx, serveSearch = false }) {
|
|
|
1957
1986
|
}
|
|
1958
1987
|
};
|
|
1959
1988
|
}
|
|
1989
|
+
function routeOf(url, base) {
|
|
1990
|
+
let path;
|
|
1991
|
+
try {
|
|
1992
|
+
path = decodeURIComponent(new URL(url, "http://localhost").pathname);
|
|
1993
|
+
} catch {
|
|
1994
|
+
return url;
|
|
1995
|
+
}
|
|
1996
|
+
if (base !== "/") {
|
|
1997
|
+
if (path === base.slice(0, -1)) path = "/";
|
|
1998
|
+
else if (path.startsWith(base)) path = path.slice(base.length - 1);
|
|
1999
|
+
}
|
|
2000
|
+
return path.length > 1 ? path.replace(/\/+$/, "") : path;
|
|
2001
|
+
}
|
|
1960
2002
|
var SOURCE_ENDPOINT = "/__seemore/source";
|
|
1961
2003
|
async function handleSource(ctx, req, res) {
|
|
1962
2004
|
if (req.method === "GET") return handleSourceRead(ctx, req, res);
|
|
1963
|
-
if (req.method === "PUT")
|
|
2005
|
+
if (req.method === "PUT") {
|
|
2006
|
+
const origin = req.headers.origin;
|
|
2007
|
+
if (origin !== void 0 && origin !== `http://${req.headers.host ?? ""}`) {
|
|
2008
|
+
return send(res, 403, { error: "Writes are only accepted from this site's own pages." });
|
|
2009
|
+
}
|
|
2010
|
+
return handleSourceWrite(ctx, req, res);
|
|
2011
|
+
}
|
|
1964
2012
|
res.setHeader("Allow", "GET, PUT");
|
|
1965
2013
|
return send(res, 405, { error: `${req.method ?? "This method"} is not allowed here.` });
|
|
1966
2014
|
}
|
|
@@ -2088,7 +2136,7 @@ function readIfExists(path) {
|
|
|
2088
2136
|
// src/node/vite/watcher.ts
|
|
2089
2137
|
import { relative as relative3 } from "path";
|
|
2090
2138
|
import chokidar from "chokidar";
|
|
2091
|
-
var CONTENT_FILE = /\.(?:mdx?|json)$/i;
|
|
2139
|
+
var CONTENT_FILE = /\.(?:mdx?|markdown|json)$/i;
|
|
2092
2140
|
var EXCLUDED_DIR = /(?:^|\/)(?:node_modules|dist|build|out|vendor|target|venv|deps|Pods|bower_components|\.[^/]+)(?:$|\/)/;
|
|
2093
2141
|
var ALWAYS_EXCLUDED_DIR = /(?:^|\/)(?:node_modules|\.git)(?:$|\/)/;
|
|
2094
2142
|
var GLOB_CHAR = /[*?[\]{}()!]/;
|
|
@@ -2152,7 +2200,7 @@ async function handleContentChange(server, ctx, event, path) {
|
|
|
2152
2200
|
ctx.warnings.flush((line) => server.config.logger.warn(line));
|
|
2153
2201
|
await reloadVirtual(server, VIRTUAL.tree);
|
|
2154
2202
|
await reloadVirtual(server, VIRTUAL.routes);
|
|
2155
|
-
if (event === "change" && /\.mdx
|
|
2203
|
+
if (event === "change" && /\.(?:mdx?|markdown)$/i.test(path)) {
|
|
2156
2204
|
await reloadFile(server, path);
|
|
2157
2205
|
}
|
|
2158
2206
|
}
|
|
@@ -2261,6 +2309,9 @@ function createViteConfig({ ctx, mode, outDir, ssrOutDir, auth = false }) {
|
|
|
2261
2309
|
conditions: mode === "dev" ? ["worker"] : void 0
|
|
2262
2310
|
},
|
|
2263
2311
|
server: {
|
|
2312
|
+
// Off rather than Vite's default, which admits every localhost origin: the dev server
|
|
2313
|
+
// has a write endpoint, and its own pages are the only callers it means to answer.
|
|
2314
|
+
cors: false,
|
|
2264
2315
|
fs: {
|
|
2265
2316
|
// The content root is normally *outside* the Vite root, and files outside `allow`
|
|
2266
2317
|
// 404 silently — the single most likely cause of "the watcher does nothing".
|
|
@@ -2311,7 +2362,7 @@ function contentHmrPlugin() {
|
|
|
2311
2362
|
name: "seemore:content-hmr",
|
|
2312
2363
|
apply: "serve",
|
|
2313
2364
|
transform(code, id) {
|
|
2314
|
-
if (!/\.mdx
|
|
2365
|
+
if (!/\.(?:mdx?|markdown)(?:$|\?)/.test(id)) return null;
|
|
2315
2366
|
return { code: `${code}
|
|
2316
2367
|
if (import.meta.hot) import.meta.hot.accept();
|
|
2317
2368
|
`, map: null };
|
|
@@ -2568,8 +2619,8 @@ async function runDev(options) {
|
|
|
2568
2619
|
}
|
|
2569
2620
|
|
|
2570
2621
|
// src/cli/export.ts
|
|
2571
|
-
import { existsSync as
|
|
2572
|
-
import { basename as basename2, dirname as dirname10, extname, join as join13, relative as relative5, resolve as resolve7 } from "path";
|
|
2622
|
+
import { existsSync as existsSync6, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, readdirSync as readdirSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2623
|
+
import { basename as basename2, dirname as dirname10, extname, isAbsolute as isAbsolute3, join as join13, relative as relative5, resolve as resolve7, sep } from "path";
|
|
2573
2624
|
import { tmpdir as tmpdir4 } from "os";
|
|
2574
2625
|
import pc4 from "picocolors";
|
|
2575
2626
|
import { build as viteBuild2 } from "vite";
|
|
@@ -2605,10 +2656,16 @@ var THEME_INIT = `<script>(function () {
|
|
|
2605
2656
|
// src/cli/export.ts
|
|
2606
2657
|
async function runExport(options) {
|
|
2607
2658
|
const target = resolve7(options.cwd, options.file);
|
|
2608
|
-
if (!
|
|
2659
|
+
if (!existsSync6(target) || !statSync(target).isFile()) {
|
|
2609
2660
|
throw new Error(`No such file: ${options.file}`);
|
|
2610
2661
|
}
|
|
2611
|
-
|
|
2662
|
+
if (options.out !== void 0 && options.outFile !== void 0) {
|
|
2663
|
+
throw new Error("--out and --out-file cannot be combined: --out names a directory, --out-file the exact file.");
|
|
2664
|
+
}
|
|
2665
|
+
const contentRoot = resolveContentRoot(options.cwd, options.root ?? dirname10(target));
|
|
2666
|
+
if (options.root !== void 0 && !isInside(canonicalise(target), contentRoot)) {
|
|
2667
|
+
throw new Error(`${options.file} is not inside the root ${options.root}.`);
|
|
2668
|
+
}
|
|
2612
2669
|
const loaded = await loadConfig({ root: contentRoot, configPath: resolveConfigPath(options) });
|
|
2613
2670
|
const config = {
|
|
2614
2671
|
...loaded.config,
|
|
@@ -2640,9 +2697,9 @@ ${errors.join("\n\n")}`);
|
|
|
2640
2697
|
const article = await prerender.renderArticle(page.url);
|
|
2641
2698
|
const runtime = await bundleRuntime();
|
|
2642
2699
|
const html = assemble({ article, css, runtime, config, outDir, contentRoot, template });
|
|
2643
|
-
const filename = `${basename2(target).replace(/\.(?:md|mdx)$/i, "")}.html`;
|
|
2644
|
-
const targetPath = options.out === void 0 ? join13(dirname10(target), filename) : join13(resolve7(options.cwd, options.out), filename);
|
|
2645
|
-
if (
|
|
2700
|
+
const filename = `${basename2(target).replace(/\.(?:md|markdown|mdx)$/i, "")}.html`;
|
|
2701
|
+
const targetPath = options.outFile !== void 0 ? resolve7(options.cwd, options.outFile) : options.out === void 0 ? join13(dirname10(target), filename) : join13(resolve7(options.cwd, options.out), filename);
|
|
2702
|
+
if (existsSync6(targetPath)) {
|
|
2646
2703
|
console.log(pc4.yellow(`seemore replacing existing ${relative5(options.cwd, targetPath) || targetPath}`));
|
|
2647
2704
|
}
|
|
2648
2705
|
mkdirSync4(dirname10(targetPath), { recursive: true });
|
|
@@ -2654,9 +2711,13 @@ ${errors.join("\n\n")}`);
|
|
|
2654
2711
|
rmSync2(ssrOutDir, { recursive: true, force: true });
|
|
2655
2712
|
}
|
|
2656
2713
|
}
|
|
2714
|
+
function isInside(file, dir) {
|
|
2715
|
+
const path = relative5(dir, file);
|
|
2716
|
+
return path !== "" && path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute3(path);
|
|
2717
|
+
}
|
|
2657
2718
|
function readBuiltCss(outDir) {
|
|
2658
2719
|
const assets = join13(outDir, "assets");
|
|
2659
|
-
const files =
|
|
2720
|
+
const files = existsSync6(assets) ? readdirSync2(assets).filter((file) => file.endsWith(".css")) : [];
|
|
2660
2721
|
if (files.length === 0) throw new Error("The build produced no stylesheet \u2014 the export would be unstyled.");
|
|
2661
2722
|
return files.map((file) => readFileSync8(join13(assets, file), "utf8")).join("\n");
|
|
2662
2723
|
}
|
|
@@ -2747,7 +2808,7 @@ function resolveAsset(src, base, outDir, contentRoot) {
|
|
|
2747
2808
|
const suffix = path.replace(/^\/+/, "");
|
|
2748
2809
|
for (const root of [outDir, contentRoot]) {
|
|
2749
2810
|
const candidate = join13(root, suffix);
|
|
2750
|
-
if (
|
|
2811
|
+
if (existsSync6(candidate) && statSync(candidate).isFile()) return candidate;
|
|
2751
2812
|
}
|
|
2752
2813
|
return void 0;
|
|
2753
2814
|
}
|
|
@@ -2775,7 +2836,7 @@ function dataUriFor(file) {
|
|
|
2775
2836
|
function faviconLink(config, contentRoot, template) {
|
|
2776
2837
|
if (config.favicon !== void 0) {
|
|
2777
2838
|
const file = join13(contentRoot, config.favicon);
|
|
2778
|
-
if (
|
|
2839
|
+
if (existsSync6(file)) {
|
|
2779
2840
|
try {
|
|
2780
2841
|
return `<link rel="icon" href="${dataUriFor(file)}" />`;
|
|
2781
2842
|
} catch {
|
|
@@ -2839,6 +2900,8 @@ Options
|
|
|
2839
2900
|
--json print one machine-readable JSON line instead of the summary (dev only)
|
|
2840
2901
|
--config <path> path to seemore.config.ts
|
|
2841
2902
|
--out <dir> build output directory (default: dist); for export, where the HTML file is written
|
|
2903
|
+
--out-file <path> export only: the exact file to write, instead of --out
|
|
2904
|
+
--root <dir> export only: the site the file belongs to (default: the file's folder)
|
|
2842
2905
|
--base <path> subpath the site is served from, e.g. /my-repo/
|
|
2843
2906
|
-h, --help show this message
|
|
2844
2907
|
-v, --version show the version
|
|
@@ -2850,6 +2913,7 @@ function normaliseHostFlag(argv) {
|
|
|
2850
2913
|
if (next !== void 0 && !next.startsWith("-")) return argv;
|
|
2851
2914
|
return [...argv.slice(0, index), "--host=", ...argv.slice(index + 1)];
|
|
2852
2915
|
}
|
|
2916
|
+
var servesUntilKilled = false;
|
|
2853
2917
|
async function main(argv = process.argv.slice(2)) {
|
|
2854
2918
|
const { values, positionals } = parseArgs({
|
|
2855
2919
|
args: normaliseHostFlag(argv),
|
|
@@ -2862,6 +2926,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2862
2926
|
json: { type: "boolean" },
|
|
2863
2927
|
config: { type: "string" },
|
|
2864
2928
|
out: { type: "string" },
|
|
2929
|
+
"out-file": { type: "string" },
|
|
2930
|
+
root: { type: "string" },
|
|
2865
2931
|
base: { type: "string" },
|
|
2866
2932
|
help: { type: "boolean", short: "h" },
|
|
2867
2933
|
version: { type: "boolean", short: "v" }
|
|
@@ -2891,9 +2957,18 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2891
2957
|
if (isExport) {
|
|
2892
2958
|
const file = rest[0];
|
|
2893
2959
|
if (file === void 0) throw new Error("Usage: seemore export <file> \u2014 name the Markdown file to export.");
|
|
2894
|
-
await runExport({
|
|
2960
|
+
await runExport({
|
|
2961
|
+
cwd: shared.cwd,
|
|
2962
|
+
file,
|
|
2963
|
+
out: values.out,
|
|
2964
|
+
outFile: values["out-file"],
|
|
2965
|
+
root: values.root,
|
|
2966
|
+
configPath: values.config,
|
|
2967
|
+
base: values.base
|
|
2968
|
+
});
|
|
2895
2969
|
return;
|
|
2896
2970
|
}
|
|
2971
|
+
servesUntilKilled = true;
|
|
2897
2972
|
await runDev({
|
|
2898
2973
|
...shared,
|
|
2899
2974
|
port: values.port === void 0 ? void 0 : Number(values.port),
|
|
@@ -2902,12 +2977,23 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2902
2977
|
json: values.json === true
|
|
2903
2978
|
});
|
|
2904
2979
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
2980
|
+
function exitAfterFlush(code) {
|
|
2981
|
+
process.exitCode = code;
|
|
2982
|
+
process.stdout.write("", () => {
|
|
2983
|
+
process.stderr.write("", () => process.exit(code));
|
|
2984
|
+
});
|
|
2985
|
+
}
|
|
2986
|
+
main().then(
|
|
2987
|
+
() => {
|
|
2988
|
+
if (!servesUntilKilled) exitAfterFlush(0);
|
|
2989
|
+
},
|
|
2990
|
+
(error) => {
|
|
2991
|
+
console.error(`
|
|
2907
2992
|
${pc5.red("seemore")} ${error instanceof Error ? error.message : String(error)}
|
|
2908
2993
|
`);
|
|
2909
|
-
|
|
2910
|
-
}
|
|
2994
|
+
exitAfterFlush(1);
|
|
2995
|
+
}
|
|
2996
|
+
);
|
|
2911
2997
|
export {
|
|
2912
2998
|
main
|
|
2913
2999
|
};
|