@mintlify/common 1.0.1299 → 1.0.1301
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/dist/mdx/getMDXOptions.js +2 -0
- package/dist/mdx/plugins/remark/index.d.ts +1 -0
- package/dist/mdx/plugins/remark/index.js +1 -0
- package/dist/mdx/plugins/remark/remarkMdxRemoveUnsafeSmil.d.ts +7 -0
- package/dist/mdx/plugins/remark/remarkMdxRemoveUnsafeSmil.js +34 -0
- package/dist/mdx/utils.d.ts +1 -0
- package/dist/mdx/utils.js +8 -0
- package/dist/navigation/filterDivisions.js +6 -2
- package/dist/navigation/filterDivisions.test.js +75 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -5,6 +5,7 @@ import { remarkMdxExpandExpressions, } from './plugins/remark/remarkMdxExpandExp
|
|
|
5
5
|
import { remarkMdxPageStyles } from './plugins/remark/remarkMdxPageStyles.js';
|
|
6
6
|
import { remarkMdxDetectJs } from './plugins/remark/remarkMdxRemoveJs.js';
|
|
7
7
|
import { remarkMdxRemoveUnknownJsx } from './plugins/remark/remarkMdxRemoveUnknownJsx/index.js';
|
|
8
|
+
import { remarkMdxRemoveUnsafeSmil } from './plugins/remark/remarkMdxRemoveUnsafeSmil.js';
|
|
8
9
|
import { remarkMermaid } from './plugins/remark/remarkMermaid.js';
|
|
9
10
|
import { remarkNoCopyLanguage } from './plugins/remark/remarkNoCopyLanguage.js';
|
|
10
11
|
import { remarkParseTitleMarkdown, remarkRenderTitleMarkdown, } from './plugins/remark/remarkTitleMarkdown.js';
|
|
@@ -70,6 +71,7 @@ export const getMDXOptions = ({ data, remarkPlugins = [], earlyRemarkPlugins = [
|
|
|
70
71
|
...remarkPlugins,
|
|
71
72
|
remarkRenderTitleMarkdown,
|
|
72
73
|
remarkMapExplicitAnchors,
|
|
74
|
+
remarkMdxRemoveUnsafeSmil,
|
|
73
75
|
[
|
|
74
76
|
remarkMdxRemoveUnknownJsx,
|
|
75
77
|
{ allowlist: ['style', ...allowedComponents, ...knownComponents] },
|
|
@@ -6,6 +6,7 @@ export * from './remarkRemoveImports.js';
|
|
|
6
6
|
export * from './remarkExtractTableOfContents.js';
|
|
7
7
|
export * from './remarkMdxRemoveUnusedVariables.js';
|
|
8
8
|
export * from './remarkMdxRemoveUnknownJsx/index.js';
|
|
9
|
+
export * from './remarkMdxRemoveUnsafeSmil.js';
|
|
9
10
|
export * from './remarkMdxExpandExpressions.js';
|
|
10
11
|
export * from './remarkMdxClientComponentBoundaries.js';
|
|
11
12
|
export * from './remarkMermaid.js';
|
|
@@ -6,6 +6,7 @@ export * from './remarkRemoveImports.js';
|
|
|
6
6
|
export * from './remarkExtractTableOfContents.js';
|
|
7
7
|
export * from './remarkMdxRemoveUnusedVariables.js';
|
|
8
8
|
export * from './remarkMdxRemoveUnknownJsx/index.js';
|
|
9
|
+
export * from './remarkMdxRemoveUnsafeSmil.js';
|
|
9
10
|
export * from './remarkMdxExpandExpressions.js';
|
|
10
11
|
export * from './remarkMdxClientComponentBoundaries.js';
|
|
11
12
|
export * from './remarkMermaid.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Root } from 'mdast';
|
|
2
|
+
import type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx';
|
|
3
|
+
type JsxElement = MdxJsxFlowElement | MdxJsxTextElement;
|
|
4
|
+
type SmilCandidate = Partial<Pick<JsxElement, 'name' | 'attributes'>>;
|
|
5
|
+
export declare const isUnsafeSmilElement: ({ name, attributes }: SmilCandidate) => boolean;
|
|
6
|
+
export declare const remarkMdxRemoveUnsafeSmil: () => (tree: Root) => void;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { remove } from 'unist-util-remove';
|
|
2
|
+
import { isMdxJsxElement, SMIL_ELEMENT_NAMES } from '../../utils.js';
|
|
3
|
+
const SMIL_ELEMENTS = new Set(SMIL_ELEMENT_NAMES.map((name) => name.toLowerCase()));
|
|
4
|
+
const ANIMATION_VALUE_ATTRIBUTES = new Set(['values', 'from', 'to', 'by']);
|
|
5
|
+
const REFERENCE_ATTRIBUTES = new Set(['href', 'xlink:href', 'xlinkhref']);
|
|
6
|
+
const SCRIPTABLE_TARGETS = new Set(['href', 'xlinkhref', 'src', 'style']);
|
|
7
|
+
const SCRIPTABLE_URL = /(?:javascript|vbscript|data):/;
|
|
8
|
+
const normalize = (value) => Array.from(value)
|
|
9
|
+
.filter((character) => character > ' ')
|
|
10
|
+
.join('')
|
|
11
|
+
.toLowerCase();
|
|
12
|
+
const targetsScriptableAttribute = (attributeName) => {
|
|
13
|
+
const localName = attributeName.slice(attributeName.lastIndexOf(':') + 1);
|
|
14
|
+
return SCRIPTABLE_TARGETS.has(localName) || localName.startsWith('on');
|
|
15
|
+
};
|
|
16
|
+
function isSafeSmilAttribute(attribute) {
|
|
17
|
+
if (attribute.type === 'mdxJsxExpressionAttribute')
|
|
18
|
+
return false;
|
|
19
|
+
const name = attribute.name.toLowerCase();
|
|
20
|
+
if (name.startsWith('on'))
|
|
21
|
+
return false;
|
|
22
|
+
if (typeof attribute.value !== 'string')
|
|
23
|
+
return attribute.value == null;
|
|
24
|
+
const value = normalize(attribute.value);
|
|
25
|
+
if (name === 'attributename')
|
|
26
|
+
return !targetsScriptableAttribute(value);
|
|
27
|
+
if (REFERENCE_ATTRIBUTES.has(name))
|
|
28
|
+
return value.startsWith('#');
|
|
29
|
+
return !ANIMATION_VALUE_ATTRIBUTES.has(name) || !SCRIPTABLE_URL.test(value);
|
|
30
|
+
}
|
|
31
|
+
export const isUnsafeSmilElement = ({ name, attributes = [] }) => name != null && SMIL_ELEMENTS.has(name.toLowerCase()) && !attributes.every(isSafeSmilAttribute);
|
|
32
|
+
export const remarkMdxRemoveUnsafeSmil = () => (tree) => {
|
|
33
|
+
remove(tree, { cascade: false }, (node) => isMdxJsxElement(node) && isUnsafeSmilElement(node));
|
|
34
|
+
};
|
package/dist/mdx/utils.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { MdxFlowExpression, MdxjsEsm, MdxJsxAttribute, MdxJsxExpressionAttribute
|
|
|
3
3
|
import type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx';
|
|
4
4
|
import type { Node } from 'unist';
|
|
5
5
|
import type { MdxNodeBodyChildType } from '../types/mdx/index.js';
|
|
6
|
+
export declare const SMIL_ELEMENT_NAMES: readonly ["animate", "animateMotion", "animateTransform", "mpath", "set"];
|
|
6
7
|
export declare const isMdxJsEsm: (node: Node) => node is MdxjsEsm;
|
|
7
8
|
export declare const isMdxExpression: (node: Node) => node is MdxFlowExpression | MdxTextExpression;
|
|
8
9
|
export declare function estreeIsProgram<T extends MdxFlowExpression | MdxTextExpression | MdxjsEsm>(node: T): node is T & {
|
package/dist/mdx/utils.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export const SMIL_ELEMENT_NAMES = [
|
|
2
|
+
'animate',
|
|
3
|
+
'animateMotion',
|
|
4
|
+
'animateTransform',
|
|
5
|
+
'mpath',
|
|
6
|
+
'set',
|
|
7
|
+
];
|
|
1
8
|
export const isMdxJsEsm = (node) => node.type === 'mdxjsEsm';
|
|
2
9
|
export const isMdxExpression = (node) => node.type === 'mdxFlowExpression' || node.type === 'mdxTextExpression';
|
|
3
10
|
export function estreeIsProgram(node) {
|
|
@@ -142,6 +149,7 @@ export const allowedComponents = [
|
|
|
142
149
|
'sup',
|
|
143
150
|
'u',
|
|
144
151
|
'svg',
|
|
152
|
+
...SMIL_ELEMENT_NAMES,
|
|
145
153
|
'table',
|
|
146
154
|
'tbody',
|
|
147
155
|
'td',
|
|
@@ -291,13 +291,17 @@ function buildDivisionItem(param) {
|
|
|
291
291
|
const divisionItem = Object.fromEntries(Object.entries(item).filter(([key]) => !keysToDrop.includes(key)));
|
|
292
292
|
// if href is absolute url, use it, otherwise use the href from the first page
|
|
293
293
|
if (!href) {
|
|
294
|
-
const
|
|
294
|
+
const userHref = 'href' in item ? item.href : undefined;
|
|
295
|
+
const hasChildNav = divisionKey === 'languages' || keysToDrop.some((key) => key in item);
|
|
296
|
+
const isUserDefinedHref = !!userHref &&
|
|
297
|
+
(isAbsoluteUrl(userHref) ||
|
|
298
|
+
(!hasChildNav && userHref.startsWith('/') && !userHref.startsWith('//')));
|
|
295
299
|
if (!isUserDefinedHref && divisionKey === 'dropdowns' && !isActive && currentPath) {
|
|
296
300
|
href =
|
|
297
301
|
(_c = (_b = findCorrespondingDropdownHref(item, currentPath, userGroups !== null && userGroups !== void 0 ? userGroups : new Set(), isPreview)) !== null && _b !== void 0 ? _b : findSamePageInDropdownVersion(page === null || page === void 0 ? void 0 : page.href, findDivisionName(item), currentPath, rootNav, userGroups !== null && userGroups !== void 0 ? userGroups : new Set(), isPreview)) !== null && _c !== void 0 ? _c : '';
|
|
298
302
|
}
|
|
299
303
|
if (!href) {
|
|
300
|
-
href = isUserDefinedHref ?
|
|
304
|
+
href = isUserDefinedHref ? userHref : (page === null || page === void 0 ? void 0 : page.href) || '/';
|
|
301
305
|
// when users switch languages, we should stay on the same page
|
|
302
306
|
// instead of switching to the first page in the new language
|
|
303
307
|
if (!isUserDefinedHref && divisionKey === 'languages') {
|
|
@@ -970,3 +970,78 @@ describe('filterDivisions dropdown corresponding pages', () => {
|
|
|
970
970
|
});
|
|
971
971
|
});
|
|
972
972
|
});
|
|
973
|
+
describe('filterDivisions root-relative division hrefs', () => {
|
|
974
|
+
const defaultArgs = {
|
|
975
|
+
decoratedNav: { groups: [] },
|
|
976
|
+
currentPath: '/guides/intro',
|
|
977
|
+
userGroups: new Set(),
|
|
978
|
+
currentVersion: '',
|
|
979
|
+
currentLanguage: undefined,
|
|
980
|
+
shouldUseDivisionMatch: true,
|
|
981
|
+
cache: {
|
|
982
|
+
dropdownCache: new Map(),
|
|
983
|
+
},
|
|
984
|
+
};
|
|
985
|
+
const guidesGroups = [{ group: 'Start', pages: [{ title: 'Intro', href: '/guides/intro' }] }];
|
|
986
|
+
const platformGroups = [
|
|
987
|
+
{ group: 'Platform', pages: [{ title: 'Analytics', href: '/platform/analytics' }] },
|
|
988
|
+
];
|
|
989
|
+
it.each(['tabs', 'anchors', 'dropdowns'])('uses a root-relative href on %s without child navigation', (key) => {
|
|
990
|
+
var _a;
|
|
991
|
+
const divisionName = { tabs: 'tab', anchors: 'anchor', dropdowns: 'dropdown' }[key];
|
|
992
|
+
const result = filterDivisions(Object.assign(Object.assign({}, defaultArgs), { decoratedNav: {
|
|
993
|
+
[key]: [
|
|
994
|
+
{ [divisionName]: 'Guides', groups: guidesGroups },
|
|
995
|
+
{ [divisionName]: 'Analytics', href: '/platform/analytics' },
|
|
996
|
+
],
|
|
997
|
+
} }));
|
|
998
|
+
expect((_a = result[key].find((d) => d.name === 'Analytics')) === null || _a === void 0 ? void 0 : _a.href).toBe('/platform/analytics');
|
|
999
|
+
});
|
|
1000
|
+
it('uses a root-relative href on a menu item without child navigation', () => {
|
|
1001
|
+
var _a, _b;
|
|
1002
|
+
const result = filterDivisions(Object.assign(Object.assign({}, defaultArgs), { decoratedNav: {
|
|
1003
|
+
tabs: [
|
|
1004
|
+
{
|
|
1005
|
+
tab: 'Guides',
|
|
1006
|
+
menu: [
|
|
1007
|
+
{ item: 'Intro', groups: guidesGroups },
|
|
1008
|
+
{ item: 'Analytics', href: '/platform/analytics' },
|
|
1009
|
+
],
|
|
1010
|
+
},
|
|
1011
|
+
{ tab: 'Platform', groups: platformGroups },
|
|
1012
|
+
],
|
|
1013
|
+
} }));
|
|
1014
|
+
const menu = (_a = result.tabs.find((t) => t.name === 'Guides')) === null || _a === void 0 ? void 0 : _a.menu;
|
|
1015
|
+
expect((_b = menu === null || menu === void 0 ? void 0 : menu.find((m) => m.name === 'Analytics')) === null || _b === void 0 ? void 0 : _b.href).toBe('/platform/analytics');
|
|
1016
|
+
});
|
|
1017
|
+
it('keeps the first page href when the division also has child navigation', () => {
|
|
1018
|
+
var _a;
|
|
1019
|
+
const result = filterDivisions(Object.assign(Object.assign({}, defaultArgs), { decoratedNav: {
|
|
1020
|
+
tabs: [
|
|
1021
|
+
{ tab: 'Guides', groups: guidesGroups },
|
|
1022
|
+
{ tab: 'Platform', href: '/elsewhere', groups: platformGroups },
|
|
1023
|
+
],
|
|
1024
|
+
} }));
|
|
1025
|
+
expect((_a = result.tabs.find((t) => t.name === 'Platform')) === null || _a === void 0 ? void 0 : _a.href).toBe('/platform/analytics');
|
|
1026
|
+
});
|
|
1027
|
+
it('leaves language hrefs on the current page', () => {
|
|
1028
|
+
var _a;
|
|
1029
|
+
const result = filterDivisions(Object.assign(Object.assign({}, defaultArgs), { decoratedNav: {
|
|
1030
|
+
languages: [
|
|
1031
|
+
{ language: 'en', groups: guidesGroups },
|
|
1032
|
+
{ language: 'fr', href: '/fr/guides/intro' },
|
|
1033
|
+
],
|
|
1034
|
+
} }));
|
|
1035
|
+
expect((_a = result.languages.find((l) => l.language === 'fr')) === null || _a === void 0 ? void 0 : _a.href).toBe('');
|
|
1036
|
+
});
|
|
1037
|
+
it('does not treat protocol-relative hrefs as internal', () => {
|
|
1038
|
+
var _a;
|
|
1039
|
+
const result = filterDivisions(Object.assign(Object.assign({}, defaultArgs), { decoratedNav: {
|
|
1040
|
+
tabs: [
|
|
1041
|
+
{ tab: 'Guides', groups: guidesGroups },
|
|
1042
|
+
{ tab: 'Other', href: '//evil.example.com' },
|
|
1043
|
+
],
|
|
1044
|
+
} }));
|
|
1045
|
+
expect((_a = result.tabs.find((t) => t.name === 'Other')) === null || _a === void 0 ? void 0 : _a.href).toBe('/');
|
|
1046
|
+
});
|
|
1047
|
+
});
|