@docsalot/previewing 0.1.0-beta.1 → 0.1.0-beta.10
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 +6 -10
- package/dist/assets/client.tar.gz +0 -0
- package/dist/constants.d.ts +3 -2
- package/dist/constants.js +9 -10
- package/dist/local-preview/helper-commands/installDepsCommand.js +36 -7
- package/dist/local-preview/helper-commands/syncLocalContentCommand.d.ts +2 -0
- package/dist/local-preview/helper-commands/syncLocalContentCommand.js +75 -0
- package/dist/local-preview/index.js +29 -115
- package/dist/local-preview/listener/categorize.js +18 -2
- package/dist/local-preview/listener/generate.js +18 -5
- package/dist/local-preview/listener/index.d.ts +1 -1
- package/dist/local-preview/listener/index.js +51 -5
- package/dist/local-preview/listener/utils/mintConfigFile.js +0 -4
- package/dist/local-preview/prod.js +22 -82
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -15
package/README.md
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @docsalot/previewing
|
|
2
2
|
|
|
3
|
-
Preview
|
|
3
|
+
Preview DocsALot docs locally.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
npm install @
|
|
7
|
+
```bash
|
|
8
|
+
npm install @docsalot/previewing
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Community
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
[](https://discord.gg/ACREKdwjG5)
|
|
16
|
-
|
|
17
|
-
_Built with 💚 by the Mintlify community._
|
|
13
|
+
Discord: https://discord.gg/Dp6EpTv4BU
|
|
Binary file
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const TARGET_CLIENT_VERSION = "v0.1.0-runtime.1";
|
|
2
2
|
export declare const INSTALL_PATH: string;
|
|
3
|
+
export declare const BUNDLED_CLIENT_TARBALL_PATH: string;
|
|
3
4
|
export declare const HOME_DIR: string;
|
|
4
|
-
export declare const
|
|
5
|
+
export declare const DOT_DOCSALOT: string;
|
|
5
6
|
export declare const VERSION_PATH: string;
|
|
6
7
|
export declare const CLIENT_PATH: string;
|
|
7
8
|
export declare const MINT_PATH: string;
|
package/dist/constants.js
CHANGED
|
@@ -3,19 +3,18 @@
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import * as url from "url";
|
|
5
5
|
import os from "os";
|
|
6
|
-
// Change this to bump to a newer version of
|
|
7
|
-
export const
|
|
6
|
+
// Change this to bump to a newer version of the bundled client.
|
|
7
|
+
export const TARGET_CLIENT_VERSION = "v0.1.0-runtime.1";
|
|
8
8
|
// package installation location
|
|
9
9
|
export const INSTALL_PATH = url.fileURLToPath(new URL(".", import.meta.url));
|
|
10
|
+
export const BUNDLED_CLIENT_TARBALL_PATH = path.join(INSTALL_PATH, "assets", "client.tar.gz");
|
|
10
11
|
export const HOME_DIR = os.homedir();
|
|
11
|
-
//
|
|
12
|
-
// Priority: DOCSALOT_PATH env var >
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
export const
|
|
16
|
-
export const
|
|
17
|
-
export const CLIENT_PATH = path.join(DOT_MINTLIFY, "client");
|
|
18
|
-
export const MINT_PATH = path.join(DOT_MINTLIFY, "mint");
|
|
12
|
+
// DOCSALOT_PATH can be overridden via environment variable.
|
|
13
|
+
// Priority: DOCSALOT_PATH env var > ~/.docsalot
|
|
14
|
+
export const DOT_DOCSALOT = process.env.DOCSALOT_PATH || path.join(HOME_DIR, ".docsalot");
|
|
15
|
+
export const VERSION_PATH = path.join(DOT_DOCSALOT, "mint", "client-version.txt");
|
|
16
|
+
export const CLIENT_PATH = path.join(DOT_DOCSALOT, "client");
|
|
17
|
+
export const MINT_PATH = path.join(DOT_DOCSALOT, "mint");
|
|
19
18
|
// command execution location
|
|
20
19
|
export const CMD_EXEC_PATH = process.cwd();
|
|
21
20
|
export const SUPPORTED_MEDIA_EXTENSIONS = [
|
|
@@ -1,11 +1,40 @@
|
|
|
1
1
|
import shell from "shelljs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import fse, { pathExists } from "fs-extra";
|
|
3
|
+
import { BUNDLED_CLIENT_TARBALL_PATH, CLIENT_PATH, MINT_PATH, TARGET_CLIENT_VERSION, VERSION_PATH, } from "../../constants.js";
|
|
4
|
+
import { buildLogger } from "../../util.js";
|
|
5
|
+
const bootstrapBundledClient = async (logger) => {
|
|
6
|
+
if (!(await pathExists(BUNDLED_CLIENT_TARBALL_PATH))) {
|
|
7
|
+
logger.fail(`Bundled client tarball is missing at: ${BUNDLED_CLIENT_TARBALL_PATH}`);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
await fse.ensureDir(CLIENT_PATH);
|
|
11
|
+
logger.text = "Extracting bundled DocsALot client...";
|
|
12
|
+
const untarResult = shell.exec(`tar -xzf "${BUNDLED_CLIENT_TARBALL_PATH}" -C "${CLIENT_PATH}" --strip-components 1`, { silent: true });
|
|
13
|
+
if (untarResult.code !== 0) {
|
|
14
|
+
logger.fail("Failed to extract bundled DocsALot client.");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
fse.writeFileSync(VERSION_PATH, TARGET_CLIENT_VERSION);
|
|
18
|
+
};
|
|
19
|
+
const shouldBootstrapClient = async () => {
|
|
20
|
+
const clientExists = await pathExists(CLIENT_PATH);
|
|
21
|
+
if (!clientExists)
|
|
22
|
+
return true;
|
|
23
|
+
if (!(await pathExists(VERSION_PATH)))
|
|
24
|
+
return true;
|
|
25
|
+
const installedVersion = fse.readFileSync(VERSION_PATH, "utf8").trim();
|
|
26
|
+
return installedVersion !== TARGET_CLIENT_VERSION;
|
|
27
|
+
};
|
|
4
28
|
const installDeps = async () => {
|
|
5
|
-
const logger = buildLogger("");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
29
|
+
const logger = buildLogger("Preparing local DocsALot runtime...");
|
|
30
|
+
await fse.ensureDir(MINT_PATH);
|
|
31
|
+
const needsBootstrap = await shouldBootstrapClient();
|
|
32
|
+
if (needsBootstrap) {
|
|
33
|
+
await fse.remove(CLIENT_PATH);
|
|
34
|
+
await bootstrapBundledClient(logger);
|
|
35
|
+
logger.succeed("DocsALot runtime installed.");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
logger.succeed("DocsALot runtime already installed.");
|
|
10
39
|
};
|
|
11
40
|
export default installDeps;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import Chalk from "chalk";
|
|
2
|
+
import mintValidation from "@docsalot/validation";
|
|
3
|
+
import fse from "fs-extra";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { promises as _promises } from "fs";
|
|
6
|
+
import { CLIENT_PATH, CMD_EXEC_PATH } from "../../constants.js";
|
|
7
|
+
import { categorizeFiles } from "../listener/categorize.js";
|
|
8
|
+
import { generateNav } from "../listener/generate.js";
|
|
9
|
+
import { isFileSizeValid } from "../listener/utils.js";
|
|
10
|
+
import createPage from "../listener/utils/createPage.js";
|
|
11
|
+
import { getConfigPath } from "../listener/utils/mintConfigFile.js";
|
|
12
|
+
const { readFile } = _promises;
|
|
13
|
+
const PROPS_PATH = path.join(CLIENT_PATH, "src", "_props");
|
|
14
|
+
const PUBLIC_PATH = path.join(CLIENT_PATH, "public");
|
|
15
|
+
const writePages = async (contentFilenames, openApiFiles) => {
|
|
16
|
+
const writes = contentFilenames.map(async (filename) => {
|
|
17
|
+
const sourcePath = path.join(CMD_EXEC_PATH, filename);
|
|
18
|
+
const targetPath = path.join(PROPS_PATH, filename);
|
|
19
|
+
const contentStr = (await readFile(sourcePath)).toString();
|
|
20
|
+
const { pageContent } = await createPage(filename, contentStr, CMD_EXEC_PATH, openApiFiles);
|
|
21
|
+
await fse.outputFile(targetPath, pageContent, { flag: "w" });
|
|
22
|
+
});
|
|
23
|
+
await Promise.all(writes);
|
|
24
|
+
};
|
|
25
|
+
const writeSnippets = async (snippets) => {
|
|
26
|
+
const writes = snippets.map(async (filename) => {
|
|
27
|
+
const sourcePath = path.join(CMD_EXEC_PATH, filename);
|
|
28
|
+
const targetPath = path.join(PROPS_PATH, filename);
|
|
29
|
+
await fse.copy(sourcePath, targetPath);
|
|
30
|
+
});
|
|
31
|
+
await Promise.all(writes);
|
|
32
|
+
};
|
|
33
|
+
const writeStaticFiles = async (staticFilenames) => {
|
|
34
|
+
for (const filename of staticFilenames) {
|
|
35
|
+
const sourcePath = path.join(CMD_EXEC_PATH, filename);
|
|
36
|
+
if (!(await isFileSizeValid(sourcePath, 5))) {
|
|
37
|
+
console.error(Chalk.red(`🚨 The file at ${filename} is too big. The maximum file size is 5 mb.`));
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const targetPath = path.join(PUBLIC_PATH, filename);
|
|
41
|
+
await fse.copy(sourcePath, targetPath);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const copyAndValidateLayout = async () => {
|
|
45
|
+
const configPath = await getConfigPath(CMD_EXEC_PATH);
|
|
46
|
+
if (configPath == null) {
|
|
47
|
+
throw new Error("Could not find layout.json in the docs directory.");
|
|
48
|
+
}
|
|
49
|
+
const rawConfig = (await readFile(configPath)).toString();
|
|
50
|
+
const config = JSON.parse(rawConfig);
|
|
51
|
+
const { status, errors, warnings } = mintValidation.validateMintConfig(config);
|
|
52
|
+
errors.forEach((error) => {
|
|
53
|
+
console.error(`🚨 ${Chalk.red(error)}`);
|
|
54
|
+
});
|
|
55
|
+
warnings.forEach((warning) => {
|
|
56
|
+
console.warn(`⚠️ ${Chalk.yellow(warning)}`);
|
|
57
|
+
});
|
|
58
|
+
if (status !== "success") {
|
|
59
|
+
throw new Error("layout.json is invalid. Fix validation errors and try again.");
|
|
60
|
+
}
|
|
61
|
+
await fse.copy(configPath, path.join(PROPS_PATH, "layout.json"));
|
|
62
|
+
};
|
|
63
|
+
const syncLocalContentCommand = async () => {
|
|
64
|
+
await fse.ensureDir(PROPS_PATH);
|
|
65
|
+
await fse.ensureDir(PUBLIC_PATH);
|
|
66
|
+
const { contentFilenames, staticFilenames, openApiFiles, snippets } = await categorizeFiles(CMD_EXEC_PATH);
|
|
67
|
+
await copyAndValidateLayout();
|
|
68
|
+
await writePages(contentFilenames, openApiFiles);
|
|
69
|
+
await writeSnippets(snippets);
|
|
70
|
+
await writeStaticFiles(staticFilenames);
|
|
71
|
+
await fse.outputFile(path.join(PROPS_PATH, "openApiFiles.json"), JSON.stringify(openApiFiles, null, 2), { flag: "w" });
|
|
72
|
+
const generatedNav = await generateNav();
|
|
73
|
+
await fse.outputFile(path.join(PROPS_PATH, "generatedNav.json"), JSON.stringify(generatedNav, null, 2), { flag: "w" });
|
|
74
|
+
};
|
|
75
|
+
export default syncLocalContentCommand;
|
|
@@ -2,158 +2,72 @@
|
|
|
2
2
|
import Chalk from "chalk";
|
|
3
3
|
import child_process from "child_process";
|
|
4
4
|
import open from "open";
|
|
5
|
-
import fse
|
|
6
|
-
import inquirer from "inquirer";
|
|
7
|
-
import { isInternetAvailable } from "is-internet-available";
|
|
8
|
-
import path from "path";
|
|
5
|
+
import fse from "fs-extra";
|
|
9
6
|
import shell from "shelljs";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { buildLogger, ensureYarn } from "../util.js";
|
|
7
|
+
import { HOME_DIR, CLIENT_PATH, CMD_EXEC_PATH, MINT_PATH, } from "../constants.js";
|
|
8
|
+
import { buildLogger } from "../util.js";
|
|
13
9
|
import listener from "./listener/index.js";
|
|
14
10
|
import { getConfigPath } from "./listener/utils/mintConfigFile.js";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
const promptForYarn = async () => {
|
|
20
|
-
const yarnInstalled = shell.which("yarn");
|
|
21
|
-
if (!yarnInstalled) {
|
|
22
|
-
await inquirer
|
|
23
|
-
.prompt([
|
|
24
|
-
{
|
|
25
|
-
type: "confirm",
|
|
26
|
-
name: "confirm",
|
|
27
|
-
message: "yarn must be globally installed. Install yarn?",
|
|
28
|
-
default: true,
|
|
29
|
-
},
|
|
30
|
-
])
|
|
31
|
-
.then(({ confirm }) => {
|
|
32
|
-
if (confirm) {
|
|
33
|
-
shell.exec("npm install --global yarn");
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
console.log("Installation cancelled.");
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
const downloadTargetMint = async (logger) => {
|
|
42
|
-
fse.emptyDirSync(MINT_PATH);
|
|
43
|
-
logger.text = "Downloading Mintlify framework...";
|
|
44
|
-
const octokit = new Octokit();
|
|
45
|
-
const downloadRes = await octokit.repos.downloadTarballArchive({
|
|
46
|
-
owner: "mintlify",
|
|
47
|
-
repo: "mint",
|
|
48
|
-
ref: TARGET_MINT_VERSION,
|
|
49
|
-
});
|
|
50
|
-
logger.text = "Extracting Mintlify framework...";
|
|
51
|
-
const TAR_PATH = path.join(MINT_PATH, "mint.tar.gz");
|
|
52
|
-
fse.writeFileSync(TAR_PATH, Buffer.from(downloadRes.data));
|
|
53
|
-
// strip-components 1 removes the top level directory from the unzipped content
|
|
54
|
-
// which is a folder with the release sha
|
|
55
|
-
fse.mkdirSync(path.join(MINT_PATH, "mint-tmp"));
|
|
56
|
-
shell.exec("tar -xzf mint.tar.gz -C mint-tmp --strip-components 1", {
|
|
57
|
-
silent: true,
|
|
58
|
-
});
|
|
59
|
-
fse.removeSync(TAR_PATH);
|
|
60
|
-
fse.moveSync(path.join(MINT_PATH, "mint-tmp", "client"), path.join(CLIENT_PATH));
|
|
61
|
-
fse.writeFileSync(VERSION_PATH, TARGET_MINT_VERSION);
|
|
62
|
-
// Delete unnecessary content downloaded from GitHub
|
|
63
|
-
fse.removeSync(path.join(MINT_PATH, "mint-tmp"));
|
|
64
|
-
logger.text = "Installing dependencies...";
|
|
65
|
-
ensureYarn(logger);
|
|
66
|
-
shell.cd(CLIENT_PATH);
|
|
67
|
-
shell.exec("yarn", { silent: true });
|
|
68
|
-
};
|
|
69
|
-
const checkForMintJson = async (logger) => {
|
|
11
|
+
import installDepsCommand from "./helper-commands/installDepsCommand.js";
|
|
12
|
+
import syncLocalContentCommand from "./helper-commands/syncLocalContentCommand.js";
|
|
13
|
+
const checkForLayoutJson = async (logger) => {
|
|
70
14
|
const configPath = await getConfigPath(CMD_EXEC_PATH);
|
|
71
15
|
if (configPath == null) {
|
|
72
|
-
logger.fail("Must be ran in a directory where a
|
|
16
|
+
logger.fail("Must be ran in a directory where a layout.json file exists.");
|
|
73
17
|
process.exit(1);
|
|
74
18
|
}
|
|
75
19
|
return;
|
|
76
20
|
};
|
|
77
21
|
const dev = async (argv) => {
|
|
78
|
-
buildLogger("DEVS");
|
|
79
22
|
shell.cd(HOME_DIR);
|
|
80
|
-
await promptForYarn();
|
|
81
23
|
const logger = buildLogger("Preparing local Docsalot instance...");
|
|
82
24
|
await fse.ensureDir(MINT_PATH);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
process.exit(1);
|
|
88
|
-
}
|
|
89
|
-
// if (internet) {
|
|
90
|
-
// const mintVersionExists = await pathExists(VERSION_PATH);
|
|
91
|
-
// let needToDownloadTargetMint = !mintVersionExists;
|
|
92
|
-
// if (mintVersionExists) {
|
|
93
|
-
// const currVersion = fse.readFileSync(VERSION_PATH, "utf8");
|
|
94
|
-
// if (currVersion !== TARGET_MINT_VERSION) {
|
|
95
|
-
// needToDownloadTargetMint = true;
|
|
96
|
-
// }
|
|
97
|
-
// }
|
|
98
|
-
// if (needToDownloadTargetMint) {
|
|
99
|
-
// await downloadTargetMint(logger);
|
|
100
|
-
// }
|
|
101
|
-
// }
|
|
102
|
-
if (!(await nodeModulesExists())) {
|
|
103
|
-
if (!internet) {
|
|
104
|
-
logger.fail(`Dependencies are missing and you are offline. Connect to the internet and run
|
|
105
|
-
|
|
106
|
-
mintlify install
|
|
107
|
-
|
|
108
|
-
`);
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
logger.fail(`Dependencies were not installed correctly, run
|
|
112
|
-
|
|
113
|
-
mintlify install
|
|
114
|
-
|
|
115
|
-
`);
|
|
116
|
-
}
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
await checkForMintJson(logger);
|
|
120
|
-
shell.cd(CLIENT_PATH);
|
|
121
|
-
const relativePath = path.relative(CLIENT_PATH, CMD_EXEC_PATH);
|
|
122
|
-
child_process.spawnSync("yarn preconfigure", [relativePath], { shell: true });
|
|
25
|
+
await installDepsCommand();
|
|
26
|
+
await checkForLayoutJson(logger);
|
|
27
|
+
logger.text = "Syncing docs content into local runtime...";
|
|
28
|
+
await syncLocalContentCommand();
|
|
123
29
|
logger.succeed("Local instance is ready. Launching your site...");
|
|
124
30
|
run(argv.port || "3000");
|
|
125
31
|
};
|
|
126
32
|
const run = (port) => {
|
|
127
33
|
shell.cd(CLIENT_PATH);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
34
|
+
const host = process.env.BIND_ALL_INTERFACES === "true" ? "0.0.0.0" : "127.0.0.1";
|
|
35
|
+
const localAdminToken = process.env.ADMIN_TOKEN || "__docsalot_local_preview__";
|
|
36
|
+
process.env.ADMIN_TOKEN = localAdminToken;
|
|
37
|
+
process.env.DOCSALOT_LOCAL_PREVIEW = "true";
|
|
38
|
+
let browserOpened = false;
|
|
39
|
+
const docsalotDevProcess = child_process.spawn("node server.js", {
|
|
134
40
|
env: {
|
|
135
41
|
...process.env,
|
|
136
42
|
PORT: port,
|
|
43
|
+
HOSTNAME: host,
|
|
44
|
+
NODE_ENV: "production",
|
|
45
|
+
ADMIN_TOKEN: localAdminToken,
|
|
46
|
+
DOCSALOT_LOCAL_PREVIEW: "true",
|
|
137
47
|
},
|
|
138
48
|
cwd: CLIENT_PATH,
|
|
139
49
|
stdio: "pipe",
|
|
140
50
|
shell: true,
|
|
141
51
|
});
|
|
142
|
-
|
|
52
|
+
docsalotDevProcess.stdout.on("data", (data) => {
|
|
143
53
|
const output = data.toString();
|
|
144
54
|
console.log(output);
|
|
145
|
-
if (
|
|
55
|
+
if (!browserOpened &&
|
|
56
|
+
(output.includes("Ready in") ||
|
|
57
|
+
output.includes("started server") ||
|
|
58
|
+
output.includes("http://localhost:"))) {
|
|
59
|
+
browserOpened = true;
|
|
146
60
|
console.log(`🌿 ${Chalk.green(`Your local preview is available at http://localhost:${port}`)}`);
|
|
147
61
|
console.log(`🌿 ${Chalk.green("Press Ctrl+C any time to stop the local preview.")}`);
|
|
148
62
|
open(`http://localhost:${port}`);
|
|
149
63
|
}
|
|
150
64
|
});
|
|
151
65
|
const onExit = () => {
|
|
152
|
-
|
|
66
|
+
docsalotDevProcess.kill("SIGINT");
|
|
153
67
|
process.exit(0);
|
|
154
68
|
};
|
|
155
69
|
process.on("SIGINT", onExit);
|
|
156
70
|
process.on("SIGTERM", onExit);
|
|
157
|
-
listener();
|
|
71
|
+
listener(port);
|
|
158
72
|
};
|
|
159
73
|
export default dev;
|
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { getFileList } from "@docsalot/prebuild";
|
|
5
5
|
import { getFileExtension, openApiCheck } from "./utils.js";
|
|
6
|
+
const shouldIgnorePath = (filename) => {
|
|
7
|
+
const normalized = filename.replace(/\\/g, "/").toLowerCase();
|
|
8
|
+
return (normalized.startsWith("/node_modules/") ||
|
|
9
|
+
normalized.includes("/node_modules/") ||
|
|
10
|
+
normalized.startsWith("/.git/") ||
|
|
11
|
+
normalized.includes("/.git/") ||
|
|
12
|
+
normalized.startsWith("/.next/") ||
|
|
13
|
+
normalized.includes("/.next/") ||
|
|
14
|
+
normalized.startsWith("/.docsalot/") ||
|
|
15
|
+
normalized.includes("/.docsalot/"));
|
|
16
|
+
};
|
|
6
17
|
export const categorizeFiles = async (contentDirectoryPath) => {
|
|
7
18
|
const allFilesInCmdExecutionPath = getFileList(contentDirectoryPath);
|
|
8
19
|
const contentFilenames = [];
|
|
@@ -11,6 +22,9 @@ export const categorizeFiles = async (contentDirectoryPath) => {
|
|
|
11
22
|
const openApiFiles = [];
|
|
12
23
|
const snippets = [];
|
|
13
24
|
allFilesInCmdExecutionPath.forEach((filename) => {
|
|
25
|
+
if (shouldIgnorePath(filename)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
14
28
|
promises.push((async () => {
|
|
15
29
|
const extension = getFileExtension(filename);
|
|
16
30
|
let isOpenApi = false;
|
|
@@ -34,7 +48,9 @@ export const categorizeFiles = async (contentDirectoryPath) => {
|
|
|
34
48
|
});
|
|
35
49
|
}
|
|
36
50
|
}
|
|
37
|
-
else if (!filename.endsWith("
|
|
51
|
+
else if (!filename.endsWith("layout.json") &&
|
|
52
|
+
!filename.endsWith("docs.json") &&
|
|
53
|
+
!isOpenApi) {
|
|
38
54
|
// all other files
|
|
39
55
|
staticFilenames.push(filename);
|
|
40
56
|
}
|
|
@@ -64,7 +80,7 @@ const supportedStaticFileExtensions = [
|
|
|
64
80
|
export const getCategory = (filePath) => {
|
|
65
81
|
filePath = filePath.toLowerCase();
|
|
66
82
|
const parsed = path.parse(filePath);
|
|
67
|
-
if (parsed.base === "
|
|
83
|
+
if (parsed.base === "layout.json") {
|
|
68
84
|
return "mintConfig";
|
|
69
85
|
}
|
|
70
86
|
const fileName = parsed.name;
|
|
@@ -27,18 +27,31 @@ const pageMetadataKeys = [
|
|
|
27
27
|
const generateDecoratedMintNavigationFromPages = (filenamePageMetadataMap, mintConfigNav) => {
|
|
28
28
|
const filenames = Object.keys(filenamePageMetadataMap);
|
|
29
29
|
const createNav = (nav) => {
|
|
30
|
+
if (!nav || !Array.isArray(nav.pages)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
30
33
|
return {
|
|
31
34
|
group: nav.group,
|
|
32
35
|
version: nav?.version,
|
|
33
|
-
pages: nav.pages
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
pages: nav.pages
|
|
37
|
+
.map((page) => {
|
|
38
|
+
if (typeof page === "string") {
|
|
39
|
+
if (filenames.includes(page)) {
|
|
40
|
+
return filenamePageMetadataMap[page];
|
|
41
|
+
}
|
|
42
|
+
// Keep local preview resilient when navigation references missing pages.
|
|
43
|
+
return null;
|
|
36
44
|
}
|
|
37
45
|
return createNav(page);
|
|
38
|
-
})
|
|
46
|
+
})
|
|
47
|
+
.filter(Boolean)
|
|
48
|
+
.map((entry) => entry),
|
|
39
49
|
};
|
|
40
50
|
};
|
|
41
|
-
return mintConfigNav
|
|
51
|
+
return (mintConfigNav || [])
|
|
52
|
+
.map((nav) => createNav(nav))
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.map((entry) => entry);
|
|
42
55
|
};
|
|
43
56
|
const createFilenamePageMetadataMap = async (contentDirectoryPath, contentFilenames, openApiFiles, clientPath, writeFiles = false) => {
|
|
44
57
|
let pagesAcc = {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const listener: () => void;
|
|
1
|
+
declare const listener: (port?: string) => void;
|
|
2
2
|
export default listener;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
+
import axios from "axios";
|
|
2
3
|
import chokidar from "chokidar";
|
|
3
4
|
import fse from "fs-extra";
|
|
4
5
|
import pathUtil from "path";
|
|
@@ -11,7 +12,49 @@ import { promises as _promises } from "fs";
|
|
|
11
12
|
import createPage from "./utils/createPage.js";
|
|
12
13
|
import { getCategory } from "./categorize.js";
|
|
13
14
|
const { readFile } = _promises;
|
|
14
|
-
const
|
|
15
|
+
const isLocalPreviewMode = process.env.DOCSALOT_LOCAL_PREVIEW === "true";
|
|
16
|
+
const localRevalidateSecret = process.env.ADMIN_TOKEN || "__docsalot_local_preview__";
|
|
17
|
+
const toPagePath = (filename) => {
|
|
18
|
+
const normalized = filename.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
19
|
+
if (!normalized.endsWith(".md") && !normalized.endsWith(".mdx")) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const withoutExtension = normalized.replace(/\.mdx?$/i, "");
|
|
23
|
+
if (withoutExtension === "index") {
|
|
24
|
+
return "/";
|
|
25
|
+
}
|
|
26
|
+
if (withoutExtension.endsWith("/index")) {
|
|
27
|
+
return "/" + withoutExtension.slice(0, -"/index".length);
|
|
28
|
+
}
|
|
29
|
+
return "/" + withoutExtension;
|
|
30
|
+
};
|
|
31
|
+
const getRevalidatePaths = (category, filename) => {
|
|
32
|
+
const paths = ["/"];
|
|
33
|
+
if (category === "page") {
|
|
34
|
+
const pagePath = toPagePath(filename);
|
|
35
|
+
if (pagePath) {
|
|
36
|
+
paths.push(pagePath);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return [...new Set(paths)];
|
|
40
|
+
};
|
|
41
|
+
const revalidateLocalPaths = async (port, paths) => {
|
|
42
|
+
if (!isLocalPreviewMode || !port || paths.length === 0) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const path of paths) {
|
|
46
|
+
try {
|
|
47
|
+
await axios.post(`http://127.0.0.1:${port}/api/revalidate`, {
|
|
48
|
+
secret: localRevalidateSecret,
|
|
49
|
+
path,
|
|
50
|
+
}, { timeout: 2000 });
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Revalidation is best-effort; avoid failing file sync when server is restarting.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const listener = (port) => {
|
|
15
58
|
chokidar
|
|
16
59
|
.watch(CMD_EXEC_PATH, {
|
|
17
60
|
ignoreInitial: true,
|
|
@@ -38,6 +81,7 @@ const listener = () => {
|
|
|
38
81
|
console.log("Static file added: ", filename);
|
|
39
82
|
break;
|
|
40
83
|
}
|
|
84
|
+
await revalidateLocalPaths(port, getRevalidatePaths(category, filename));
|
|
41
85
|
}
|
|
42
86
|
catch (error) {
|
|
43
87
|
console.error(error.message);
|
|
@@ -63,6 +107,7 @@ const listener = () => {
|
|
|
63
107
|
console.log("Static file edited: ", filename);
|
|
64
108
|
break;
|
|
65
109
|
}
|
|
110
|
+
await revalidateLocalPaths(port, getRevalidatePaths(category, filename));
|
|
66
111
|
}
|
|
67
112
|
catch (error) {
|
|
68
113
|
console.error(error.message);
|
|
@@ -86,7 +131,7 @@ const listener = () => {
|
|
|
86
131
|
console.log(`Snippet deleted: ${filename}`);
|
|
87
132
|
break;
|
|
88
133
|
case "mintConfig":
|
|
89
|
-
console.log("⚠️
|
|
134
|
+
console.log("⚠️ layout.json deleted. Please create a new layout.json file as it is mandatory.");
|
|
90
135
|
process.exit(1);
|
|
91
136
|
case "potentialJsonOpenApiSpec":
|
|
92
137
|
case "potentialYamlOpenApiSpec":
|
|
@@ -97,6 +142,7 @@ const listener = () => {
|
|
|
97
142
|
console.log("Static file deleted: ", filename);
|
|
98
143
|
break;
|
|
99
144
|
}
|
|
145
|
+
await revalidateLocalPaths(port, getRevalidatePaths(potentialCategory, filename));
|
|
100
146
|
}
|
|
101
147
|
catch (error) {
|
|
102
148
|
console.error(error.message);
|
|
@@ -147,9 +193,9 @@ const onUpdateEvent = async (filename) => {
|
|
|
147
193
|
break;
|
|
148
194
|
case "mintConfig":
|
|
149
195
|
regenerateNav = true;
|
|
150
|
-
const
|
|
196
|
+
const layoutJsonFileContent = (await readFile(filePath)).toString();
|
|
151
197
|
try {
|
|
152
|
-
const mintConfig = JSON.parse(
|
|
198
|
+
const mintConfig = JSON.parse(layoutJsonFileContent);
|
|
153
199
|
const { status, errors, warnings } = mintValidation.validateMintConfig(mintConfig);
|
|
154
200
|
errors.forEach((error) => {
|
|
155
201
|
console.error(`🚨 ${Chalk.red(error)}`);
|
|
@@ -163,7 +209,7 @@ const onUpdateEvent = async (filename) => {
|
|
|
163
209
|
}
|
|
164
210
|
catch (error) {
|
|
165
211
|
if (error.name === "SyntaxError") {
|
|
166
|
-
console.error(`🚨 ${Chalk.red("
|
|
212
|
+
console.error(`🚨 ${Chalk.red("layout.json has invalid JSON. You are likely missing a comma or a bracket. You can paste your layout.json file into https://jsonlint.com/ to get a more specific error message.")}`);
|
|
167
213
|
}
|
|
168
214
|
else {
|
|
169
215
|
console.error(`🚨 ${Chalk.red(error.message)}`);
|
|
@@ -4,10 +4,6 @@ import pathUtil from "path";
|
|
|
4
4
|
const { readFile } = _promises;
|
|
5
5
|
// TODO: Put in prebuild package
|
|
6
6
|
export const getConfigPath = async (contentDirectoryPath) => {
|
|
7
|
-
// Prefer modern config filename, but keep backward compatibility.
|
|
8
|
-
if (await pathExists(pathUtil.join(contentDirectoryPath, "mint.json"))) {
|
|
9
|
-
return pathUtil.join(contentDirectoryPath, "mint.json");
|
|
10
|
-
}
|
|
11
7
|
if (await pathExists(pathUtil.join(contentDirectoryPath, "layout.json"))) {
|
|
12
8
|
return pathUtil.join(contentDirectoryPath, "layout.json");
|
|
13
9
|
}
|