@mintlify/astro 0.1.3 → 0.1.4
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 +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -18
- package/dist/types.d.ts +0 -11
- package/dist/utils/copy-assets.d.ts +1 -1
- package/dist/utils/copy-assets.js +19 -25
- package/dist/utils/rewrite-asset-paths.d.ts +1 -0
- package/dist/utils/rewrite-asset-paths.js +76 -0
- package/dist/utils/serve-docs-assets.d.ts +2 -0
- package/dist/utils/serve-docs-assets.js +41 -0
- package/dist/utils/static-asset-utils.d.ts +2 -0
- package/dist/utils/static-asset-utils.js +47 -0
- package/dist/utils/static-assets.d.ts +2 -0
- package/dist/utils/static-assets.js +27 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,13 +39,13 @@ import { components } from '@mintlify/astro/components';
|
|
|
39
39
|
- `./docs/docs.json`
|
|
40
40
|
- `./docs/**/*.mdx`
|
|
41
41
|
- `./docs/snippets/*` (optional)
|
|
42
|
-
- assets
|
|
42
|
+
- supported static assets anywhere in `./docs/**` (`.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.svg`, `.ico`, `.mp4`, `.webm`, `.mp3`, `.wav`, `.css`, `.woff`, `.woff2`, `.ttf`, `.eot`)
|
|
43
43
|
|
|
44
44
|
At build setup time, the integration generates:
|
|
45
45
|
|
|
46
|
-
- `.mintlify/docs/**/*`
|
|
47
|
-
- `.mintlify/components/**/*`
|
|
46
|
+
- `.mintlify/docs/**/*` (synced page content)
|
|
47
|
+
- `.mintlify/components/**/*` (extracted React components)
|
|
48
|
+
- `.mintlify/static/**/*` (static assets from docs)
|
|
48
49
|
- `.mintlify/generated/favicons.json` (when favicon is configured)
|
|
49
50
|
|
|
50
51
|
These directories are generated build artifacts and should not be edited manually.
|
|
51
|
-
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cp } from 'node:fs/promises';
|
|
1
2
|
import { join, resolve } from 'node:path';
|
|
2
3
|
import { fileURLToPath } from 'node:url';
|
|
3
4
|
import { getAllPathsInDocsNav } from '@mintlify/common';
|
|
@@ -8,43 +9,33 @@ import { loadAllSnippets } from './utils/load-all-snippets.js';
|
|
|
8
9
|
import { initLogger } from './utils/logger.js';
|
|
9
10
|
import { readDocsConfig } from './utils/read-docs-config.js';
|
|
10
11
|
import { readPageContent } from './utils/read-page-content.js';
|
|
12
|
+
import { createStaticAssetMiddleware } from './utils/serve-docs-assets.js';
|
|
11
13
|
import { writePageContent } from './utils/write-page-content.js';
|
|
12
|
-
import { writeFaviconsManifest } from './utils/write-favicons-manifest.js';
|
|
13
14
|
export function mintlify(options) {
|
|
15
|
+
let docsDir;
|
|
16
|
+
let staticDir;
|
|
14
17
|
return {
|
|
15
18
|
name: '@mintlify/astro',
|
|
16
19
|
hooks: {
|
|
17
20
|
'astro:config:setup': async ({ config: astroConfig, logger }) => {
|
|
18
21
|
initLogger(logger);
|
|
19
22
|
const rootDir = fileURLToPath(astroConfig.root);
|
|
20
|
-
const publicDir = fileURLToPath(astroConfig.publicDir);
|
|
21
23
|
const contentDir = join(rootDir, '.mintlify', 'docs');
|
|
22
24
|
const componentsDir = join(rootDir, '.mintlify', 'components');
|
|
23
|
-
|
|
25
|
+
staticDir = join(rootDir, '.mintlify', 'static');
|
|
26
|
+
docsDir = resolve(rootDir, options?.docsDir ?? './docs');
|
|
24
27
|
await Promise.all([
|
|
25
28
|
clearContentDirectory(contentDir),
|
|
26
29
|
clearContentDirectory(componentsDir),
|
|
30
|
+
clearContentDirectory(staticDir),
|
|
27
31
|
]);
|
|
28
|
-
await copyAssets(docsDir,
|
|
32
|
+
const assetCount = await copyAssets(docsDir, staticDir);
|
|
33
|
+
logger.info(`Copied ${assetCount} static assets`);
|
|
29
34
|
const snippets = await loadAllSnippets(docsDir);
|
|
30
35
|
logger.info(`Loaded ${snippets.length} snippet files`);
|
|
31
36
|
const userComponents = await findUserComponents(docsDir);
|
|
32
37
|
const docsConfig = await readDocsConfig(docsDir);
|
|
33
38
|
const pagePaths = getAllPathsInDocsNav(docsConfig.navigation);
|
|
34
|
-
if (docsConfig.favicon) {
|
|
35
|
-
const faviconHref = typeof docsConfig.favicon === 'string'
|
|
36
|
-
? docsConfig.favicon
|
|
37
|
-
: docsConfig.favicon.light;
|
|
38
|
-
await writeFaviconsManifest(rootDir, {
|
|
39
|
-
icons: [
|
|
40
|
-
{
|
|
41
|
-
rel: 'icon',
|
|
42
|
-
type: 'image/svg+xml',
|
|
43
|
-
href: faviconHref,
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
39
|
logger.info(`Found ${pagePaths.length} pages to sync`);
|
|
49
40
|
await Promise.all(pagePaths.map(async (pagePath) => {
|
|
50
41
|
try {
|
|
@@ -64,6 +55,13 @@ export function mintlify(options) {
|
|
|
64
55
|
}));
|
|
65
56
|
logger.info(`Synced ${pagePaths.length} pages`);
|
|
66
57
|
},
|
|
58
|
+
'astro:server:setup': ({ server }) => {
|
|
59
|
+
server.middlewares.use(createStaticAssetMiddleware(staticDir));
|
|
60
|
+
},
|
|
61
|
+
'astro:build:done': async ({ dir }) => {
|
|
62
|
+
const outputDir = fileURLToPath(dir);
|
|
63
|
+
await cp(staticDir, outputDir, { recursive: true });
|
|
64
|
+
},
|
|
67
65
|
},
|
|
68
66
|
};
|
|
69
67
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,12 +1 @@
|
|
|
1
1
|
export type { DocsConfig, NavigationConfig } from '@mintlify/validation';
|
|
2
|
-
export interface FaviconIcon {
|
|
3
|
-
rel: string;
|
|
4
|
-
sizes?: string;
|
|
5
|
-
type: string;
|
|
6
|
-
href: string;
|
|
7
|
-
media?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface Favicons {
|
|
10
|
-
icons: FaviconIcon[];
|
|
11
|
-
browserconfig?: string;
|
|
12
|
-
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function copyAssets(docsDir: string,
|
|
1
|
+
export declare function copyAssets(docsDir: string, outputDir: string): Promise<number>;
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import { cp, stat } from 'node:fs/promises';
|
|
1
|
+
import { cp, mkdir, readdir, stat } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
try {
|
|
5
|
-
await stat(path);
|
|
6
|
-
return true;
|
|
7
|
-
}
|
|
8
|
-
catch {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
3
|
+
import { isSupportedStaticAsset } from './static-assets.js';
|
|
12
4
|
async function isDirectory(path) {
|
|
13
5
|
try {
|
|
14
6
|
const stats = await stat(path);
|
|
@@ -18,19 +10,21 @@ async function isDirectory(path) {
|
|
|
18
10
|
return false;
|
|
19
11
|
}
|
|
20
12
|
}
|
|
21
|
-
async function
|
|
22
|
-
if (!(await isDirectory(
|
|
23
|
-
return;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
13
|
+
export async function copyAssets(docsDir, outputDir) {
|
|
14
|
+
if (!(await isDirectory(docsDir)))
|
|
15
|
+
return 0;
|
|
16
|
+
let count = 0;
|
|
17
|
+
const copyDirectory = async (sourceDir, targetDir) => {
|
|
18
|
+
const entries = await readdir(sourceDir, { withFileTypes: true });
|
|
19
|
+
const files = entries.filter((e) => e.isFile() && isSupportedStaticAsset(e.name));
|
|
20
|
+
const subdirs = entries.filter((e) => e.isDirectory());
|
|
21
|
+
if (files.length > 0) {
|
|
22
|
+
await mkdir(targetDir, { recursive: true });
|
|
23
|
+
await Promise.all(files.map((entry) => cp(join(sourceDir, entry.name), join(targetDir, entry.name))));
|
|
24
|
+
count += files.length;
|
|
25
|
+
}
|
|
26
|
+
await Promise.all(subdirs.map((entry) => copyDirectory(join(sourceDir, entry.name), join(targetDir, entry.name))));
|
|
27
|
+
};
|
|
28
|
+
await copyDirectory(docsDir, outputDir);
|
|
29
|
+
return count;
|
|
36
30
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function rewriteAssetPaths(content: string, filename: string): string;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { getAST, stringifyTree } from '@mintlify/common';
|
|
2
|
+
import { posix } from 'node:path';
|
|
3
|
+
import { visit } from 'unist-util-visit';
|
|
4
|
+
import { hasSupportedStaticAssetExtension } from './static-asset-utils.js';
|
|
5
|
+
const MINTLIFY_ASSET_PREFIX = '/_mintlify';
|
|
6
|
+
function isMdxJsxAttribute(attr) {
|
|
7
|
+
return (typeof attr === 'object' &&
|
|
8
|
+
attr !== null &&
|
|
9
|
+
'type' in attr &&
|
|
10
|
+
attr.type === 'mdxJsxAttribute');
|
|
11
|
+
}
|
|
12
|
+
function splitPathAndSuffix(url) {
|
|
13
|
+
const queryIndex = url.indexOf('?');
|
|
14
|
+
const hashIndex = url.indexOf('#');
|
|
15
|
+
let splitIndex = -1;
|
|
16
|
+
if (queryIndex === -1) {
|
|
17
|
+
splitIndex = hashIndex;
|
|
18
|
+
}
|
|
19
|
+
else if (hashIndex === -1) {
|
|
20
|
+
splitIndex = queryIndex;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
splitIndex = Math.min(queryIndex, hashIndex);
|
|
24
|
+
}
|
|
25
|
+
if (splitIndex === -1) {
|
|
26
|
+
return { pathname: url, suffix: '' };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
pathname: url.slice(0, splitIndex),
|
|
30
|
+
suffix: url.slice(splitIndex),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function rewriteRootAssetUrl(url) {
|
|
34
|
+
if (!url.startsWith('/') ||
|
|
35
|
+
url.startsWith('//') ||
|
|
36
|
+
url === MINTLIFY_ASSET_PREFIX ||
|
|
37
|
+
url.startsWith(`${MINTLIFY_ASSET_PREFIX}/`)) {
|
|
38
|
+
return url;
|
|
39
|
+
}
|
|
40
|
+
const { pathname, suffix } = splitPathAndSuffix(url);
|
|
41
|
+
if (hasSupportedStaticAssetExtension(pathname)) {
|
|
42
|
+
return `${posix.join(MINTLIFY_ASSET_PREFIX, pathname)}${suffix}`;
|
|
43
|
+
}
|
|
44
|
+
return url;
|
|
45
|
+
}
|
|
46
|
+
function rewriteJsxAttributes(node) {
|
|
47
|
+
if (!node.attributes)
|
|
48
|
+
return;
|
|
49
|
+
for (const attr of node.attributes) {
|
|
50
|
+
if (!isMdxJsxAttribute(attr))
|
|
51
|
+
continue;
|
|
52
|
+
if (typeof attr.value !== 'string')
|
|
53
|
+
continue;
|
|
54
|
+
if (attr.name !== 'src' && attr.name !== 'href')
|
|
55
|
+
continue;
|
|
56
|
+
attr.value = rewriteRootAssetUrl(attr.value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function rewriteAssetPaths(content, filename) {
|
|
60
|
+
const tree = getAST(content, filename);
|
|
61
|
+
visit(tree, 'image', (node) => {
|
|
62
|
+
node.url = rewriteRootAssetUrl(node.url);
|
|
63
|
+
});
|
|
64
|
+
visit(tree, 'link', (node) => {
|
|
65
|
+
node.url = rewriteRootAssetUrl(node.url);
|
|
66
|
+
});
|
|
67
|
+
visit(tree, 'definition', (node) => {
|
|
68
|
+
node.url = rewriteRootAssetUrl(node.url);
|
|
69
|
+
});
|
|
70
|
+
visit(tree, (node) => {
|
|
71
|
+
if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') {
|
|
72
|
+
rewriteJsxAttributes(node);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return stringifyTree(tree);
|
|
76
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
3
|
+
import { getMimeType, isSupportedStaticAsset } from './static-assets.js';
|
|
4
|
+
export function createStaticAssetMiddleware(staticDir) {
|
|
5
|
+
const normalizedDir = resolve(staticDir);
|
|
6
|
+
return async (req, res, next) => {
|
|
7
|
+
const url = req.url;
|
|
8
|
+
if (!url)
|
|
9
|
+
return next();
|
|
10
|
+
let pathname;
|
|
11
|
+
try {
|
|
12
|
+
pathname = decodeURIComponent(new URL(url, 'http://localhost').pathname);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return next();
|
|
16
|
+
}
|
|
17
|
+
const relativePath = pathname.startsWith('/') ? pathname.slice(1) : pathname;
|
|
18
|
+
if (!relativePath)
|
|
19
|
+
return next();
|
|
20
|
+
if (!isSupportedStaticAsset(relativePath))
|
|
21
|
+
return next();
|
|
22
|
+
const filePath = resolve(normalizedDir, relativePath);
|
|
23
|
+
const relativeToRoot = relative(normalizedDir, filePath);
|
|
24
|
+
if (relativeToRoot.startsWith('..') || isAbsolute(relativeToRoot))
|
|
25
|
+
return next();
|
|
26
|
+
try {
|
|
27
|
+
const stats = await stat(filePath);
|
|
28
|
+
if (!stats.isFile())
|
|
29
|
+
return next();
|
|
30
|
+
const content = await readFile(filePath);
|
|
31
|
+
const mimeType = getMimeType(pathname);
|
|
32
|
+
if (mimeType) {
|
|
33
|
+
res.setHeader('Content-Type', mimeType);
|
|
34
|
+
}
|
|
35
|
+
res.end(content);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
next();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { posix } from 'node:path';
|
|
2
|
+
const SUPPORTED_STATIC_ASSET_EXTENSIONS = new Set([
|
|
3
|
+
'.png',
|
|
4
|
+
'.jpg',
|
|
5
|
+
'.jpeg',
|
|
6
|
+
'.gif',
|
|
7
|
+
'.webp',
|
|
8
|
+
'.svg',
|
|
9
|
+
'.ico',
|
|
10
|
+
'.mp4',
|
|
11
|
+
'.webm',
|
|
12
|
+
'.mp3',
|
|
13
|
+
'.wav',
|
|
14
|
+
'.css',
|
|
15
|
+
'.js',
|
|
16
|
+
'.woff',
|
|
17
|
+
'.woff2',
|
|
18
|
+
'.ttf',
|
|
19
|
+
'.eot',
|
|
20
|
+
]);
|
|
21
|
+
const MIME_TYPES = {
|
|
22
|
+
'.png': 'image/png',
|
|
23
|
+
'.jpg': 'image/jpeg',
|
|
24
|
+
'.jpeg': 'image/jpeg',
|
|
25
|
+
'.gif': 'image/gif',
|
|
26
|
+
'.webp': 'image/webp',
|
|
27
|
+
'.svg': 'image/svg+xml',
|
|
28
|
+
'.ico': 'image/x-icon',
|
|
29
|
+
'.mp4': 'video/mp4',
|
|
30
|
+
'.webm': 'video/webm',
|
|
31
|
+
'.mp3': 'audio/mpeg',
|
|
32
|
+
'.wav': 'audio/wav',
|
|
33
|
+
'.css': 'text/css',
|
|
34
|
+
'.js': 'application/javascript',
|
|
35
|
+
'.woff': 'font/woff',
|
|
36
|
+
'.woff2': 'font/woff2',
|
|
37
|
+
'.ttf': 'font/ttf',
|
|
38
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
39
|
+
};
|
|
40
|
+
export function hasSupportedStaticAssetExtension(pathname) {
|
|
41
|
+
const extension = posix.extname(pathname).toLowerCase();
|
|
42
|
+
return SUPPORTED_STATIC_ASSET_EXTENSIONS.has(extension);
|
|
43
|
+
}
|
|
44
|
+
export function getMimeType(pathname) {
|
|
45
|
+
const extension = posix.extname(pathname).toLowerCase();
|
|
46
|
+
return MIME_TYPES[extension];
|
|
47
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { posix } from 'node:path';
|
|
2
|
+
const SUPPORTED_STATIC_ASSETS = {
|
|
3
|
+
'.png': 'image/png',
|
|
4
|
+
'.jpg': 'image/jpeg',
|
|
5
|
+
'.jpeg': 'image/jpeg',
|
|
6
|
+
'.gif': 'image/gif',
|
|
7
|
+
'.webp': 'image/webp',
|
|
8
|
+
'.svg': 'image/svg+xml',
|
|
9
|
+
'.ico': 'image/x-icon',
|
|
10
|
+
'.mp4': 'video/mp4',
|
|
11
|
+
'.webm': 'video/webm',
|
|
12
|
+
'.mp3': 'audio/mpeg',
|
|
13
|
+
'.wav': 'audio/wav',
|
|
14
|
+
'.css': 'text/css',
|
|
15
|
+
'.woff': 'font/woff',
|
|
16
|
+
'.woff2': 'font/woff2',
|
|
17
|
+
'.ttf': 'font/ttf',
|
|
18
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
19
|
+
};
|
|
20
|
+
export function isSupportedStaticAsset(pathname) {
|
|
21
|
+
const extension = posix.extname(pathname).toLowerCase();
|
|
22
|
+
return extension in SUPPORTED_STATIC_ASSETS;
|
|
23
|
+
}
|
|
24
|
+
export function getMimeType(pathname) {
|
|
25
|
+
const extension = posix.extname(pathname).toLowerCase();
|
|
26
|
+
return SUPPORTED_STATIC_ASSETS[extension];
|
|
27
|
+
}
|