MCPcopy Create free account
hub / github.com/observablehq/framework / findFiles

Function findFiles

src/javascript/files.ts:39–120  ·  view source on GitHub ↗
(
  body: Node,
  path: string,
  input: string,
  aliases?: Iterable<string> // ["FileAttachment"] for implicit import
)

Source from the content-addressed store, hash-verified

37 * passed a dynamic argument, or references a file that is outside the root).
38 */
39export function findFiles(
40 body: Node,
41 path: string,
42 input: string,
43 aliases?: Iterable<string> // ["FileAttachment"] for implicit import
44): FileExpression[] {
45 const declarations = new Set<{name: string}>();
46 const alias = new Set<string>(aliases);
47 let globals: Set<string> | undefined;
48
49 // Find the declared local names of FileAttachment. Currently only named
50 // imports are supported, and stdlib must be imported without a version. TODO
51 // Support namespace imports? Error if stdlib is expressed with a version?
52 simple(body, {
53 ImportDeclaration(node) {
54 if (node.source.value === "observablehq:stdlib" || node.source.value === "npm:@observablehq/stdlib") {
55 for (const specifier of node.specifiers) {
56 if (
57 specifier.type === "ImportSpecifier" &&
58 specifier.imported.type === "Identifier" &&
59 specifier.imported.name === "FileAttachment"
60 ) {
61 declarations.add(specifier.local);
62 alias.add(specifier.local.name);
63 }
64 }
65 }
66 }
67 });
68
69 // If the import is masking a global, don’t treat it as a global (since we’ll
70 // ignore the import declaration below).
71 for (const name of alias.keys()) {
72 if (defaultGlobals.has(name)) {
73 (globals ??= new Set(defaultGlobals)).delete(name);
74 }
75 }
76
77 // Collect all references to FileAttachment.
78 const references = new Set(
79 findReferences(body, {
80 globals,
81 filterDeclaration: (identifier) => !declarations.has(identifier) // treat the imported declaration as unbound
82 })
83 );
84
85 const files: FileExpression[] = [];
86
87 // Find all calls to FileAttachment. If the call is part of a member
88 // expression such as FileAttachment("foo.txt").csv, use this to determine the
89 // file method ("csv"); otherwise fallback to the to file extension to
90 // determine the method. Also enforce that FileAttachment is passed a single
91 // static string literal.
92 //
93 // Note that while dynamic imports require that paths start with ./, ../, or
94 // /, the same requirement is not true for file attachments. Unlike imports,
95 // you can’t reference a global file as a file attachment.
96 ancestor(body, {

Callers 3

transpileModuleFunction · 0.70
getModuleInfoFunction · 0.70
parseJavaScriptFunction · 0.70

Calls 2

findReferencesFunction · 0.85
deleteMethod · 0.80

Tested by

no test coverage detected