@mintlify/common 1.0.1269 → 1.0.1271
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/estreeReferences.d.ts +39 -0
- package/dist/mdx/estreeReferences.js +963 -0
- package/dist/mdx/plugins/remark/firstPartyMarkdownComponents.d.ts +3 -0
- package/dist/mdx/plugins/remark/firstPartyMarkdownComponents.js +29 -0
- package/dist/mdx/plugins/remark/remarkMdxRemoveUnknownJsx/index.d.ts +1 -1
- package/dist/mdx/plugins/remark/remarkVisibilityForMarkdown.d.ts +7 -1
- package/dist/mdx/plugins/remark/remarkVisibilityForMarkdown.js +581 -177
- package/dist/mdx/plugins/remark/staticEvaluator.d.ts +55 -0
- package/dist/mdx/plugins/remark/staticEvaluator.js +639 -0
- package/dist/mdx/plugins/remark/unfoldComponentsForMarkdown.d.ts +71 -0
- package/dist/mdx/plugins/remark/unfoldComponentsForMarkdown.js +1414 -0
- package/dist/mdx/remark-core.d.ts +7 -0
- package/dist/mdx/remark-core.js +32 -0
- package/dist/mdx/remark.d.ts +1 -7
- package/dist/mdx/remark.js +2 -32
- package/dist/mdx/snippets/getExportMap.js +33 -5
- package/dist/mdx/snippets/resolveAllImports.js +3 -2
- package/dist/mdx/snippets/resolveImport/exportCollision.d.ts +1 -0
- package/dist/mdx/snippets/resolveImport/exportCollision.js +2 -1
- package/dist/mdx/snippets/resolveImport/index.d.ts +11 -1
- package/dist/mdx/snippets/resolveImport/index.js +129 -16
- package/dist/mdx/snippets/resolveImport/resolveComponentWithContent.js +38 -5
- package/dist/mdx/snippets/resolveImport/snippetDeclarations.d.ts +56 -0
- package/dist/mdx/snippets/resolveImport/snippetDeclarations.js +655 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Root } from 'mdast';
|
|
2
|
+
import type { PluggableList } from 'unified';
|
|
3
|
+
export declare const coreRemarkMdxPlugins: PluggableList;
|
|
4
|
+
export declare const coreRemark: import("unified").Processor<Root, undefined, undefined, Root, string>;
|
|
5
|
+
export declare const getAST: (str: string, filePath?: string) => Root;
|
|
6
|
+
export declare const stringifyTree: (tree: Root) => string;
|
|
7
|
+
export declare const stringifyTreeForAgents: (tree: Root) => string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { remark } from 'remark';
|
|
2
|
+
import remarkFrontmatter from 'remark-frontmatter';
|
|
3
|
+
import remarkGfm from 'remark-gfm';
|
|
4
|
+
import remarkMath from 'remark-math';
|
|
5
|
+
import remarkMdx from 'remark-mdx';
|
|
6
|
+
import remarkStringify from 'remark-stringify';
|
|
7
|
+
import { preprocessCustomHeadingIds } from './preprocessCustomHeadingIds.js';
|
|
8
|
+
import { preprocessMdxFragmentCode } from './preprocessMdxFragmentCode.js';
|
|
9
|
+
import { getJsxEsmTree } from './snippets/getJsxEsmTree.js';
|
|
10
|
+
import { isJsxOrTsx } from './snippets/isJsxOrTsx.js';
|
|
11
|
+
export const coreRemarkMdxPlugins = [
|
|
12
|
+
remarkMdx,
|
|
13
|
+
remarkGfm,
|
|
14
|
+
[remarkFrontmatter, ['yaml', 'toml']],
|
|
15
|
+
remarkMath,
|
|
16
|
+
];
|
|
17
|
+
export const coreRemark = remark().use(coreRemarkMdxPlugins).freeze();
|
|
18
|
+
const agentRemarkMdxPlugins = [
|
|
19
|
+
remarkMdx,
|
|
20
|
+
[remarkGfm, { tablePipeAlign: false }],
|
|
21
|
+
[remarkFrontmatter, ['yaml', 'toml']],
|
|
22
|
+
remarkMath,
|
|
23
|
+
];
|
|
24
|
+
const agentRemark = remark().use(agentRemarkMdxPlugins).freeze();
|
|
25
|
+
export const getAST = (str, filePath) => {
|
|
26
|
+
if (isJsxOrTsx(filePath)) {
|
|
27
|
+
return getJsxEsmTree(str, filePath);
|
|
28
|
+
}
|
|
29
|
+
return coreRemark().parse(preprocessCustomHeadingIds(preprocessMdxFragmentCode(str)));
|
|
30
|
+
};
|
|
31
|
+
export const stringifyTree = (tree) => coreRemark().use(remarkStringify).stringify(tree);
|
|
32
|
+
export const stringifyTreeForAgents = (tree) => agentRemark().use(remarkStringify).stringify(tree);
|
package/dist/mdx/remark.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import type { Root } from 'mdast';
|
|
2
|
-
import type { PluggableList } from 'unified';
|
|
3
1
|
import { type RemarkUserMentionsOptions } from './plugins/remark/remarkUserMentions.js';
|
|
4
|
-
export
|
|
5
|
-
export declare const coreRemark: import("unified").Processor<Root, undefined, undefined, Root, string>;
|
|
6
|
-
export declare const getAST: (str: string, filePath?: string) => Root;
|
|
7
|
-
export declare const stringifyTree: (tree: Root) => string;
|
|
8
|
-
export declare const stringifyTreeForAgents: (tree: Root) => string;
|
|
2
|
+
export { coreRemark, coreRemarkMdxPlugins, getAST, stringifyTree, stringifyTreeForAgents, } from './remark-core.js';
|
|
9
3
|
export declare function stripVisibilityForMarkdown(mdx: string): string;
|
|
10
4
|
export declare function transformContentForMarkdown(mdx: string, mentionOptions?: RemarkUserMentionsOptions): string;
|
package/dist/mdx/remark.js
CHANGED
|
@@ -1,37 +1,7 @@
|
|
|
1
|
-
import { remark } from 'remark';
|
|
2
|
-
import remarkFrontmatter from 'remark-frontmatter';
|
|
3
|
-
import remarkGfm from 'remark-gfm';
|
|
4
|
-
import remarkMath from 'remark-math';
|
|
5
|
-
import remarkMdx from 'remark-mdx';
|
|
6
|
-
import remarkStringify from 'remark-stringify';
|
|
7
1
|
import { remarkUserMentions, } from './plugins/remark/remarkUserMentions.js';
|
|
8
2
|
import { remarkVisibilityForMarkdown } from './plugins/remark/remarkVisibilityForMarkdown.js';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
import { getJsxEsmTree } from './snippets/getJsxEsmTree.js';
|
|
12
|
-
import { isJsxOrTsx } from './snippets/isJsxOrTsx.js';
|
|
13
|
-
export const coreRemarkMdxPlugins = [
|
|
14
|
-
remarkMdx,
|
|
15
|
-
remarkGfm,
|
|
16
|
-
[remarkFrontmatter, ['yaml', 'toml']],
|
|
17
|
-
remarkMath,
|
|
18
|
-
];
|
|
19
|
-
export const coreRemark = remark().use(coreRemarkMdxPlugins).freeze();
|
|
20
|
-
const agentRemarkMdxPlugins = [
|
|
21
|
-
remarkMdx,
|
|
22
|
-
[remarkGfm, { tablePipeAlign: false }],
|
|
23
|
-
[remarkFrontmatter, ['yaml', 'toml']],
|
|
24
|
-
remarkMath,
|
|
25
|
-
];
|
|
26
|
-
const agentRemark = remark().use(agentRemarkMdxPlugins).freeze();
|
|
27
|
-
export const getAST = (str, filePath) => {
|
|
28
|
-
if (isJsxOrTsx(filePath)) {
|
|
29
|
-
return getJsxEsmTree(str, filePath);
|
|
30
|
-
}
|
|
31
|
-
return coreRemark().parse(preprocessCustomHeadingIds(preprocessMdxFragmentCode(str)));
|
|
32
|
-
};
|
|
33
|
-
export const stringifyTree = (tree) => coreRemark().use(remarkStringify).stringify(tree);
|
|
34
|
-
export const stringifyTreeForAgents = (tree) => agentRemark().use(remarkStringify).stringify(tree);
|
|
3
|
+
import { getAST, stringifyTreeForAgents } from './remark-core.js';
|
|
4
|
+
export { coreRemark, coreRemarkMdxPlugins, getAST, stringifyTree, stringifyTreeForAgents, } from './remark-core.js';
|
|
35
5
|
export function stripVisibilityForMarkdown(mdx) {
|
|
36
6
|
const tree = getAST(mdx);
|
|
37
7
|
remarkVisibilityForMarkdown()(tree);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, toJs } from 'estree-util-to-js';
|
|
2
2
|
import { visit } from 'unist-util-visit';
|
|
3
|
+
import { collectPatternNames } from '../estreeReferences.js';
|
|
3
4
|
import { moduleExportName } from './importedExportName.js';
|
|
4
5
|
import { nodeIncludesExport } from './nodeIncludesExport.js';
|
|
5
6
|
import { addExportPrefix } from './resolveImport/addExportPrefix.js';
|
|
@@ -11,6 +12,7 @@ export const getExportMapFromTree = (tree) => {
|
|
|
11
12
|
var _a;
|
|
12
13
|
const exportMap = {};
|
|
13
14
|
visit(tree, (node) => {
|
|
15
|
+
var _a;
|
|
14
16
|
if (!nodeIncludesExport(node))
|
|
15
17
|
return;
|
|
16
18
|
for (const bodyChild of node.data.estree.body) {
|
|
@@ -39,8 +41,8 @@ export const getExportMapFromTree = (tree) => {
|
|
|
39
41
|
for (const specifier of bodyChild.specifiers) {
|
|
40
42
|
const localName = specifier.local.name;
|
|
41
43
|
const exportedName = moduleExportName(specifier.exported);
|
|
42
|
-
// Find the original declaration
|
|
43
|
-
const originalDeclaration = findDeclarationInTree(localName, node);
|
|
44
|
+
// Find the original declaration, which may itself be exported
|
|
45
|
+
const originalDeclaration = (_a = findDeclarationInTree(localName, node)) !== null && _a !== void 0 ? _a : findExportedDeclaration(localName, tree);
|
|
44
46
|
if (originalDeclaration) {
|
|
45
47
|
const wrappedNode = structuredClone(node);
|
|
46
48
|
wrappedNode.data.estree.body = [originalDeclaration];
|
|
@@ -55,15 +57,15 @@ export const getExportMapFromTree = (tree) => {
|
|
|
55
57
|
// Variable declarations
|
|
56
58
|
if (bodyChild.declaration.type === 'VariableDeclaration') {
|
|
57
59
|
for (const declaration of bodyChild.declaration.declarations) {
|
|
58
|
-
if (declaration.id.type !== 'Identifier')
|
|
59
|
-
continue;
|
|
60
60
|
if (declaration.init == null)
|
|
61
61
|
continue;
|
|
62
62
|
// Sometimes when more than one exports are defined next to each other, the body includes multiple exports so we isolate the export we are looking for.
|
|
63
63
|
const isolatedExport = structuredClone(node);
|
|
64
64
|
isolatedExport.data.estree.body = [bodyChild];
|
|
65
65
|
const value = toJs(isolatedExport.data.estree, { handlers: jsx }).value;
|
|
66
|
-
|
|
66
|
+
// A destructured declaration binds every name in its pattern with the same statement
|
|
67
|
+
for (const name of patternNames(declaration.id))
|
|
68
|
+
exportMap[name] = value;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
// Function declarations
|
|
@@ -89,6 +91,32 @@ export const getExportMapFromTree = (tree) => {
|
|
|
89
91
|
}
|
|
90
92
|
return exportMap;
|
|
91
93
|
};
|
|
94
|
+
function patternNames(pattern) {
|
|
95
|
+
const names = new Set();
|
|
96
|
+
collectPatternNames(pattern, names);
|
|
97
|
+
return names;
|
|
98
|
+
}
|
|
99
|
+
function findExportedDeclaration(name, tree) {
|
|
100
|
+
let found = null;
|
|
101
|
+
visit(tree, (node) => {
|
|
102
|
+
var _a;
|
|
103
|
+
if (found !== null || !nodeIncludesExport(node))
|
|
104
|
+
return;
|
|
105
|
+
for (const bodyChild of node.data.estree.body) {
|
|
106
|
+
if (bodyChild.type !== 'ExportNamedDeclaration' || bodyChild.declaration == null)
|
|
107
|
+
continue;
|
|
108
|
+
const declaration = bodyChild.declaration;
|
|
109
|
+
const declares = declaration.type === 'VariableDeclaration'
|
|
110
|
+
? declaration.declarations.some((declarator) => patternNames(declarator.id).has(name))
|
|
111
|
+
: ((_a = declaration.id) === null || _a === void 0 ? void 0 : _a.name) === name;
|
|
112
|
+
if (declares) {
|
|
113
|
+
found = bodyChild;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
return found;
|
|
119
|
+
}
|
|
92
120
|
/**
|
|
93
121
|
* Find a declaration by name in the tree
|
|
94
122
|
*/
|
|
@@ -16,7 +16,7 @@ import { estreeIsProgram, isImportDeclaration, isMdxJsEsm } from '../utils.js';
|
|
|
16
16
|
import { findAndRemoveImports } from './findAndRemoveImports.js';
|
|
17
17
|
import { getExportMapFromTree } from './getExportMap.js';
|
|
18
18
|
import { getSpecifierPackageName } from './isBareSpecifier.js';
|
|
19
|
-
import { resolveImport } from './resolveImport/index.js';
|
|
19
|
+
import { createImportResolutionState, resolveImport } from './resolveImport/index.js';
|
|
20
20
|
import { injectToTopOfFileOfTree } from './resolveImport/injectToTopOfFile.js';
|
|
21
21
|
import { resolveImportPath } from './resolveImportPath.js';
|
|
22
22
|
const hasImportDeclaration = (node) => isMdxJsEsm(node) && estreeIsProgram(node) && node.data.estree.body.some(isImportDeclaration);
|
|
@@ -119,6 +119,7 @@ export const resolveAllImports = (params) => __awaiter(void 0, void 0, void 0, f
|
|
|
119
119
|
? takePackageReExports(ast, packages.packageNames)
|
|
120
120
|
: [];
|
|
121
121
|
const exportMap = getExportMapFromTree(ast);
|
|
122
|
+
const importState = createImportResolutionState();
|
|
122
123
|
const strippedSnippets = new Map();
|
|
123
124
|
for (const source of Object.keys(fileWithImports.importMap)) {
|
|
124
125
|
const resolvedPath = resolveImportPath(source, fileWithImports.filename);
|
|
@@ -198,7 +199,7 @@ export const resolveAllImports = (params) => __awaiter(void 0, void 0, void 0, f
|
|
|
198
199
|
else {
|
|
199
200
|
console.log(located.message);
|
|
200
201
|
}
|
|
201
|
-
});
|
|
202
|
+
}, importState);
|
|
202
203
|
if (contentWithResolvedImport == undefined) {
|
|
203
204
|
throw new Error('Import failed to resolve');
|
|
204
205
|
}
|
|
@@ -3,5 +3,6 @@ export type ExportCollisionWarning = {
|
|
|
3
3
|
message: string;
|
|
4
4
|
};
|
|
5
5
|
export type OnExportCollision = (warning: ExportCollisionWarning) => void;
|
|
6
|
+
export declare const isSameStatement: (left: string, right: string) => boolean;
|
|
6
7
|
/** Same name, different code: the first declaration wins, so say so instead of failing silently. */
|
|
7
8
|
export declare const warnOnExportCollision: (name: string, existing: string, incoming: string, onWarning?: OnExportCollision) => void;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const normalizeStatement = (statement) => statement.replace(/\s+/g, ' ').trim();
|
|
2
|
+
export const isSameStatement = (left, right) => normalizeStatement(left) === normalizeStatement(right);
|
|
2
3
|
/** Same name, different code: the first declaration wins, so say so instead of failing silently. */
|
|
3
4
|
export const warnOnExportCollision = (name, existing, incoming, onWarning) => {
|
|
4
|
-
if (
|
|
5
|
+
if (isSameStatement(existing, incoming))
|
|
5
6
|
return;
|
|
6
7
|
onWarning === null || onWarning === void 0 ? void 0 : onWarning({
|
|
7
8
|
type: 'export-collision',
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import type { Root } from 'mdast';
|
|
2
2
|
import type { ImportSpecifier } from '../../../types/mdx/snippets/import.js';
|
|
3
|
+
import { collectContextFromTree } from '../../plugins/remark/staticEvaluator.js';
|
|
3
4
|
import { type OnExportCollision } from './exportCollision.js';
|
|
4
5
|
export { type ExportCollisionWarning, type OnExportCollision } from './exportCollision.js';
|
|
5
6
|
export { findExport } from './findExport.js';
|
|
6
7
|
export { injectToTopOfFileOfTree } from './injectToTopOfFile.js';
|
|
8
|
+
/** What one page's imports have resolved so far. Pass the same state for every import of a page. */
|
|
9
|
+
export interface ImportResolutionState {
|
|
10
|
+
/** The local name the page declares for each binding of each imported snippet */
|
|
11
|
+
bindings: Map<Root, Map<string, string>>;
|
|
12
|
+
/** Evaluated module scope of each imported snippet */
|
|
13
|
+
contexts: Map<Root, ReturnType<typeof collectContextFromTree>>;
|
|
14
|
+
}
|
|
15
|
+
export declare const createImportResolutionState: () => ImportResolutionState;
|
|
7
16
|
/**
|
|
8
17
|
*
|
|
9
18
|
* @param importSpecifier The name of the imported component we want to replace
|
|
10
19
|
* @param destinationPageContent The content of the page we want to inject the import into
|
|
11
20
|
* @param importedFileContent The content of the file we are importing from
|
|
21
|
+
* @param state Shared by every import of one page, so readers of a snippet binding share one value
|
|
12
22
|
* @returns
|
|
13
23
|
*/
|
|
14
|
-
export declare const resolveImport: (importSpecifier: ImportSpecifier, destinationPageContent: Root, importedFileContent: Root, exportMap: Record<string, string>, onWarning?: OnExportCollision) => Promise<Root | undefined>;
|
|
24
|
+
export declare const resolveImport: (importSpecifier: ImportSpecifier, destinationPageContent: Root, importedFileContent: Root, exportMap: Record<string, string>, onWarning?: OnExportCollision, state?: ImportResolutionState) => Promise<Root | undefined>;
|
|
@@ -8,18 +8,39 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { MdxImportSpecifier } from '../../../types/mdx/snippets/import.js';
|
|
11
|
+
import { collectContextFromTree, serializeStaticValue, } from '../../plugins/remark/staticEvaluator.js';
|
|
11
12
|
import { getAST } from '../../remark.js';
|
|
12
13
|
import { getExportMapFromTree } from '../getExportMap.js';
|
|
13
14
|
import { warnOnExportCollision } from './exportCollision.js';
|
|
14
15
|
import { findExport } from './findExport.js';
|
|
15
16
|
import { injectToTopOfFileOfTree } from './injectToTopOfFile.js';
|
|
16
17
|
import { resolveComponentWithContent } from './resolveComponentWithContent.js';
|
|
18
|
+
import { collectExportedNames, collectTopLevelBindingNames, declarationSource, exportedBindingName, injectBelowEagerDependencies, isolateExportedDeclarator, patternSiblingNames, prepareSnippetSiblings, } from './snippetDeclarations.js';
|
|
17
19
|
export { findExport } from './findExport.js';
|
|
18
20
|
export { injectToTopOfFileOfTree } from './injectToTopOfFile.js';
|
|
19
21
|
const VALID_SPECIFIERS = [
|
|
20
22
|
MdxImportSpecifier.ImportSpecifier,
|
|
21
23
|
MdxImportSpecifier.RenamedImportSpecifier,
|
|
22
24
|
];
|
|
25
|
+
export const createImportResolutionState = () => ({
|
|
26
|
+
bindings: new Map(),
|
|
27
|
+
contexts: new Map(),
|
|
28
|
+
});
|
|
29
|
+
/** The local names this page already declares for each binding of `snippet`. */
|
|
30
|
+
function heldSnippetBindings(state, snippet) {
|
|
31
|
+
var _a;
|
|
32
|
+
const held = (_a = state.bindings.get(snippet)) !== null && _a !== void 0 ? _a : new Map();
|
|
33
|
+
state.bindings.set(snippet, held);
|
|
34
|
+
return held;
|
|
35
|
+
}
|
|
36
|
+
function snippetContext(state, tree) {
|
|
37
|
+
const cached = state.contexts.get(tree);
|
|
38
|
+
if (cached !== undefined)
|
|
39
|
+
return cached;
|
|
40
|
+
const context = collectContextFromTree(tree);
|
|
41
|
+
state.contexts.set(tree, context);
|
|
42
|
+
return context;
|
|
43
|
+
}
|
|
23
44
|
const OTHER_SPECIFIERS = [
|
|
24
45
|
MdxImportSpecifier.ImportDefaultSpecifier,
|
|
25
46
|
MdxImportSpecifier.ImportNamespaceSpecifier,
|
|
@@ -29,29 +50,121 @@ const OTHER_SPECIFIERS = [
|
|
|
29
50
|
* @param importSpecifier The name of the imported component we want to replace
|
|
30
51
|
* @param destinationPageContent The content of the page we want to inject the import into
|
|
31
52
|
* @param importedFileContent The content of the file we are importing from
|
|
53
|
+
* @param state Shared by every import of one page, so readers of a snippet binding share one value
|
|
32
54
|
* @returns
|
|
33
55
|
*/
|
|
34
|
-
export const resolveImport = (
|
|
35
|
-
var _a, _b;
|
|
56
|
+
export const resolveImport = (importSpecifier_1, destinationPageContent_1, importedFileContent_1, exportMap_1, onWarning_1, ...args_1) => __awaiter(void 0, [importSpecifier_1, destinationPageContent_1, importedFileContent_1, exportMap_1, onWarning_1, ...args_1], void 0, function* (importSpecifier, destinationPageContent, importedFileContent, exportMap, onWarning, state = createImportResolutionState()) {
|
|
57
|
+
var _a, _b, _c;
|
|
36
58
|
if (VALID_SPECIFIERS.includes(importSpecifier.type)) {
|
|
37
59
|
// normal import: import { Component } from '/snippets/Component.mdx' or jsx file
|
|
38
|
-
// find "Component" in "/snippets/Component.mdx" or jsx files
|
|
39
|
-
const exportContent = findExport(importSpecifier.name, importedFileContent, importSpecifier.renamedName);
|
|
40
|
-
if (exportContent == undefined)
|
|
41
|
-
throw new Error(`Could not find export ${importSpecifier.name} in snippet`);
|
|
42
|
-
// `import { X as Y }` binds Y; an existing declaration of that name wins
|
|
43
60
|
const localName = (_a = importSpecifier.renamedName) !== null && _a !== void 0 ? _a : importSpecifier.name;
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
const destinationBindings = collectTopLevelBindingNames(destinationPageContent);
|
|
62
|
+
const destinationExports = collectExportedNames(destinationPageContent);
|
|
63
|
+
// An existing binding or export of the name wins; warn when its definition differs. MDX
|
|
64
|
+
// modules cannot declare unexported bindings, so a page export alias also keeps the name.
|
|
65
|
+
const keepExisting = (incoming) => {
|
|
66
|
+
var _a, _b;
|
|
67
|
+
const injected = exportMap[localName];
|
|
68
|
+
if (injected !== undefined) {
|
|
69
|
+
warnOnExportCollision(localName, injected, incoming, onWarning);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (!destinationBindings.has(localName) && !destinationExports.has(localName))
|
|
73
|
+
return false;
|
|
74
|
+
warnOnExportCollision(localName, (_a = declarationSource(destinationPageContent, localName)) !== null && _a !== void 0 ? _a : '', (_b = declarationSource(getAST(incoming), localName)) !== null && _b !== void 0 ? _b : incoming, onWarning);
|
|
75
|
+
return true;
|
|
76
|
+
};
|
|
77
|
+
const binding = exportedBindingName(importedFileContent, importSpecifier.name);
|
|
78
|
+
const held = heldSnippetBindings(state, importedFileContent);
|
|
79
|
+
const holder = held.get(binding);
|
|
80
|
+
// A binding this page already declares, explicitly or as a private sibling, is aliased so
|
|
81
|
+
// every reader shares one value, as it would in a real module
|
|
82
|
+
if (holder !== undefined && holder !== localName) {
|
|
83
|
+
const statement = `export const ${localName} = ${holder};`;
|
|
84
|
+
if (keepExisting(statement))
|
|
85
|
+
return destinationPageContent;
|
|
86
|
+
injectBelowEagerDependencies(destinationPageContent, getAST(statement).children);
|
|
87
|
+
exportMap[localName] = statement;
|
|
88
|
+
return destinationPageContent;
|
|
89
|
+
}
|
|
90
|
+
const exportContent = findExport(importSpecifier.name, structuredClone(importedFileContent), importSpecifier.renamedName);
|
|
91
|
+
if (exportContent == undefined) {
|
|
92
|
+
const sourceScope = snippetContext(state, importedFileContent).moduleScope;
|
|
93
|
+
if (!collectExportedNames(importedFileContent).has(importSpecifier.name) ||
|
|
94
|
+
!sourceScope.has(binding)) {
|
|
95
|
+
throw new Error(`Could not find export ${importSpecifier.name} in snippet`);
|
|
96
|
+
}
|
|
97
|
+
const serialize = (name) => {
|
|
98
|
+
const stored = sourceScope.get(name);
|
|
99
|
+
return serializeStaticValue(stored === undefined ? undefined : stored);
|
|
100
|
+
};
|
|
101
|
+
const statement = `export const ${localName} = ${serialize(binding)};`;
|
|
102
|
+
if (keepExisting(statement))
|
|
103
|
+
return destinationPageContent;
|
|
104
|
+
// The other names a destructuring export binds are declared privately too, so a sibling
|
|
105
|
+
// that reads one later shares it instead of copying the whole pattern again
|
|
106
|
+
const siblings = [];
|
|
107
|
+
for (const [name, privateName] of patternSiblingNames(importedFileContent, binding)) {
|
|
108
|
+
if (held.has(name) || !sourceScope.has(name))
|
|
109
|
+
continue;
|
|
110
|
+
const sibling = `export const ${privateName} = ${serialize(name)};`;
|
|
111
|
+
siblings.push(sibling);
|
|
112
|
+
(_b = exportMap[privateName]) !== null && _b !== void 0 ? _b : (exportMap[privateName] = sibling);
|
|
113
|
+
held.set(name, privateName);
|
|
114
|
+
}
|
|
115
|
+
injectToTopOfFileOfTree(destinationPageContent, getAST([...siblings, statement].join('\n')).children);
|
|
116
|
+
exportMap[localName] = statement;
|
|
117
|
+
held.set(binding, localName);
|
|
47
118
|
return destinationPageContent;
|
|
48
119
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
120
|
+
if (keepExisting(exportContent))
|
|
121
|
+
return destinationPageContent;
|
|
122
|
+
const statementTree = getAST(exportContent);
|
|
123
|
+
// A grouped statement is injected whole unless another of its names is already taken.
|
|
124
|
+
// `findExport` keeps the binding's original name when renaming it is unsafe, so the local name
|
|
125
|
+
// is then declared as an alias and no other declarator may take it.
|
|
126
|
+
const exportedNames = collectTopLevelBindingNames(statementTree);
|
|
127
|
+
const exportedDeclarator = exportedNames.has(binding) ? binding : localName;
|
|
128
|
+
const localAlias = exportedDeclarator === localName
|
|
129
|
+
? []
|
|
130
|
+
: getAST(`export const ${localName} = ${exportedDeclarator};`).children;
|
|
131
|
+
const redeclaresDestination = [...exportedNames].some((declared) => declared !== exportedDeclarator &&
|
|
132
|
+
(declared === localName ||
|
|
133
|
+
held.has(declared) ||
|
|
134
|
+
destinationBindings.has(declared) ||
|
|
135
|
+
destinationExports.has(declared)));
|
|
136
|
+
const isolated = redeclaresDestination
|
|
137
|
+
? isolateExportedDeclarator(statementTree, exportedDeclarator)
|
|
138
|
+
: statementTree;
|
|
139
|
+
const { siblings, statement, trailing, privateNames } = prepareSnippetSiblings(destinationPageContent, importedFileContent, isolated, { name: importSpecifier.name, localName }, held);
|
|
140
|
+
const injected = {
|
|
141
|
+
type: 'root',
|
|
142
|
+
children: [...siblings, ...statement.children, ...localAlias, ...trailing],
|
|
143
|
+
};
|
|
144
|
+
injectBelowEagerDependencies(destinationPageContent, injected.children);
|
|
145
|
+
// `findExport` keeps the original name when renaming the declaration is unsafe. The binding's
|
|
146
|
+
// own name is checked first, since a grouped statement can also declare a different
|
|
147
|
+
// declarator under the local name.
|
|
148
|
+
const statementNames = collectTopLevelBindingNames(statement);
|
|
149
|
+
if (statementNames.has(binding))
|
|
150
|
+
held.set(binding, binding);
|
|
151
|
+
else if (statementNames.has(localName))
|
|
152
|
+
held.set(binding, localName);
|
|
153
|
+
// A grouped statement declares its other snippet bindings on the page under their own names
|
|
154
|
+
const snippetBindings = collectTopLevelBindingNames(importedFileContent);
|
|
155
|
+
for (const declared of statementNames) {
|
|
156
|
+
if (declared !== localName && snippetBindings.has(declared) && !held.has(declared)) {
|
|
157
|
+
held.set(declared, declared);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (const [original, privateName] of privateNames) {
|
|
161
|
+
if (!held.has(original))
|
|
162
|
+
held.set(original, privateName);
|
|
163
|
+
}
|
|
164
|
+
// Record every exported name, private siblings included, so a later import or inlined
|
|
165
|
+
// snippet dedupes against it
|
|
166
|
+
for (const [declared, source] of Object.entries(getExportMapFromTree(injected))) {
|
|
167
|
+
(_c = exportMap[declared]) !== null && _c !== void 0 ? _c : (exportMap[declared] = source);
|
|
55
168
|
}
|
|
56
169
|
return destinationPageContent;
|
|
57
170
|
}
|
|
@@ -15,8 +15,8 @@ import { JSXParser } from '../../lib/jsxParser.js';
|
|
|
15
15
|
import { getAST } from '../../remark.js';
|
|
16
16
|
import { createUniqueVariableName, isMdxJsxFlowElement } from '../../utils.js';
|
|
17
17
|
import { findAndRemoveExports } from '../findAndRemoveExports.js';
|
|
18
|
-
import { warnOnExportCollision } from './exportCollision.js';
|
|
19
|
-
import {
|
|
18
|
+
import { isSameStatement, warnOnExportCollision, } from './exportCollision.js';
|
|
19
|
+
import { injectBelowEagerDependencies, isPrivateSiblingName, pruneDeclaredNames, statementSourceNames, takePrivateDeclarations, } from './snippetDeclarations.js';
|
|
20
20
|
/**
|
|
21
21
|
*
|
|
22
22
|
* @param tree The tree to inject into
|
|
@@ -274,19 +274,52 @@ const replaceCodePlaceholdersWithProps = (tree, propValues) => {
|
|
|
274
274
|
});
|
|
275
275
|
};
|
|
276
276
|
const reinsertExports = (content, exportMap, snippetExportMap, onWarning) => {
|
|
277
|
+
var _a;
|
|
277
278
|
const nodesToInject = [];
|
|
279
|
+
const injectedStatements = new Set();
|
|
278
280
|
for (const [key, value] of Object.entries(snippetExportMap)) {
|
|
279
281
|
const existing = exportMap[key];
|
|
282
|
+
// An alias maps to its local's declaration, which does not declare the alias itself. The
|
|
283
|
+
// declaration is still inserted when nothing else supplies it, but the alias is not claimed.
|
|
284
|
+
const declaresKey = key === 'default' || statementSourceNames(value).has(key);
|
|
285
|
+
if (existing == undefined && !declaresKey) {
|
|
286
|
+
const declared = [...statementSourceNames(value)];
|
|
287
|
+
const unsupplied = declared.every((name) => exportMap[name] === undefined && snippetExportMap[name] === undefined);
|
|
288
|
+
if (unsupplied && !injectedStatements.has(value)) {
|
|
289
|
+
nodesToInject.push(...getAST(value).children);
|
|
290
|
+
injectedStatements.add(value);
|
|
291
|
+
for (const name of declared)
|
|
292
|
+
exportMap[name] = value;
|
|
293
|
+
}
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
280
296
|
if (existing == undefined) {
|
|
281
|
-
|
|
282
|
-
|
|
297
|
+
// A destructured export maps each of its names to the same statement: insert it once, and
|
|
298
|
+
// without the names the page already declares
|
|
299
|
+
const taken = new Set([...statementSourceNames(value)].filter((name) => name !== key && exportMap[name] !== undefined && exportMap[name] !== value));
|
|
300
|
+
const statement = taken.size === 0
|
|
301
|
+
? value
|
|
302
|
+
: pruneDeclaredNames(value, taken, (name) => exportMap[name] !== undefined);
|
|
303
|
+
if (statement === undefined) {
|
|
304
|
+
for (const name of taken)
|
|
305
|
+
warnOnExportCollision(name, (_a = exportMap[name]) !== null && _a !== void 0 ? _a : '', value, onWarning);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (!injectedStatements.has(statement))
|
|
309
|
+
nodesToInject.push(...getAST(statement).children);
|
|
310
|
+
injectedStatements.add(statement);
|
|
283
311
|
exportMap[key] = value;
|
|
284
312
|
}
|
|
313
|
+
else if (isPrivateSiblingName(key) && isSameStatement(existing, value)) {
|
|
314
|
+
// The same snippet helper, already injected: move it up so this snippet's code reads it
|
|
315
|
+
// after it is declared. Different code under a reserved name falls through to a warning.
|
|
316
|
+
nodesToInject.push(...takePrivateDeclarations(content, [key]));
|
|
317
|
+
}
|
|
285
318
|
else {
|
|
286
319
|
warnOnExportCollision(key, existing, value, onWarning);
|
|
287
320
|
}
|
|
288
321
|
}
|
|
289
322
|
if (nodesToInject.length > 0) {
|
|
290
|
-
|
|
323
|
+
injectBelowEagerDependencies(content, nodesToInject);
|
|
291
324
|
}
|
|
292
325
|
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Root, RootContent } from 'mdast';
|
|
2
|
+
export declare function isolateExportedDeclarator(tree: Root, name: string): Root;
|
|
3
|
+
/** Names a module makes importable: named export declarations, specifiers, and default. */
|
|
4
|
+
export declare function collectExportedNames(tree: Root): Set<string>;
|
|
5
|
+
/** Every top-level lexical binding in the tree's ESM statements, exported or not. */
|
|
6
|
+
export declare function collectTopLevelBindingNames(tree: Root): Set<string>;
|
|
7
|
+
/** The local binding behind an export name, following `export { local as name }`. */
|
|
8
|
+
export declare function exportedBindingName(tree: Root, exportName: string): string;
|
|
9
|
+
/** Source of the top-level declaration that binds `name`, without any `export` keyword. */
|
|
10
|
+
export declare function declarationSource(tree: Root, name: string): string | undefined;
|
|
11
|
+
/** Names an export statement's source declares, destructured patterns included. */
|
|
12
|
+
export declare function statementSourceNames(source: string): Set<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Rewrites a single variable export so it no longer declares the names in `drop`. A declarator
|
|
15
|
+
* that only binds dropped names and that no kept declarator reads is removed, so its initializer
|
|
16
|
+
* never runs. Any other dropped name is renamed to a private name throughout the statement, so
|
|
17
|
+
* rest elements still receive the same keys and kept declarators still read the snippet's values.
|
|
18
|
+
*/
|
|
19
|
+
export declare function pruneDeclaredNames(source: string, drop: ReadonlySet<string>, isNameTaken?: (name: string) => boolean): string | undefined;
|
|
20
|
+
export declare function isPrivateSiblingName(name: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Removes the private sibling declarations for `names` from the tree, with the private siblings
|
|
23
|
+
* they reference, and returns them dependencies first so they can be re-inserted above new readers.
|
|
24
|
+
*/
|
|
25
|
+
export declare function takePrivateDeclarations(tree: Root, names: Iterable<string>): RootContent[];
|
|
26
|
+
/**
|
|
27
|
+
* Injects each node as high as it can go: at the top of the page, or just below the last
|
|
28
|
+
* declaration it reads while initializing, such as a reused helper that could not move above a
|
|
29
|
+
* page binding. Placing nodes one at a time keeps helpers that need no such dependency at the top,
|
|
30
|
+
* above any existing code that reads them.
|
|
31
|
+
*/
|
|
32
|
+
export declare function injectBelowEagerDependencies(tree: Root, nodes: RootContent[]): void;
|
|
33
|
+
export type SnippetInjection = {
|
|
34
|
+
/** Declared above the imported statement. */
|
|
35
|
+
siblings: RootContent[];
|
|
36
|
+
statement: Root;
|
|
37
|
+
/** Declared below the imported statement, because they read it while initializing. */
|
|
38
|
+
trailing: RootContent[];
|
|
39
|
+
/** The final name of each snippet binding that is now declared privately on the page. */
|
|
40
|
+
privateNames: Map<string, string>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Collects the snippet declarations the injected statement transitively references. They stay
|
|
44
|
+
* private to the import, as in a real module: each gets a reserved name derived from its snippet,
|
|
45
|
+
* and dependencies are declared first. A sibling an earlier import already injected is reused and
|
|
46
|
+
* moved up with its own dependencies, so it stays above every reader.
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* The private names that the other bindings of `binding`'s declarator take on a page, such as
|
|
50
|
+
* `other` for `export const [binding, other] = …`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function patternSiblingNames(imported: Root, binding: string): Map<string, string>;
|
|
53
|
+
export declare function prepareSnippetSiblings(destination: Root, imported: Root, statement: Root, requested: {
|
|
54
|
+
name: string;
|
|
55
|
+
localName: string;
|
|
56
|
+
}, heldBindings?: ReadonlyMap<string, string>): SnippetInjection;
|