summaryrefslogtreecommitdiff
path: root/src/compiler/codegen/content.ts
blob: fb8f9e3079019e182c3c3fe907eca61aa98a730b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import path from 'path';
import { fdir, PathsOutput } from 'fdir';

/**
 * Handling for import.meta.glob and import.meta.globEager
 */

interface GlobOptions {
  namespace: string;
  filename: string;
}

interface GlobResult {
  /** Array of import statements to inject */
  imports: Set<string>;
  /** Replace original code with */
  code: string;
}

const crawler = new fdir();

/** General glob handling */
function globSearch(spec: string, { filename }: { filename: string }): string[] {
  try {
    // Note: fdir’s glob requires you to do some work finding the closest non-glob folder.
    // For example, this fails: .glob("./post/*.md").crawl("/…/src/pages") ❌
    //       …but this doesn’t: .glob("*.md").crawl("/…/src/pages/post")   ✅
    let globDir = '';
    let glob = spec;
    for (const part of spec.split('/')) {
      if (!part.includes('*')) {
        // iterate through spec until first '*' is reached
        globDir = path.posix.join(globDir, part); // this must be POSIX-style
        glob = glob.replace(`${part}/`, ''); // move parent dirs off spec, and onto globDir
      } else {
        // at first '*', exit
        break;
      }
    }

    const cwd = path.join(path.dirname(filename), globDir.replace(/\//g, path.sep)); // this must match OS (could be '/' or '\')
    let found = crawler.glob(glob).crawl(cwd).sync() as PathsOutput;
    if (!found.length) {
      throw new Error(`No files matched "${spec}" from ${filename}`);
    }
    return found.map((importPath) => {
      if (importPath.startsWith('http') || importPath.startsWith('.')) return importPath;
      return `./` + globDir + '/' + importPath;
    });
  } catch (err) {
    throw new Error(`No files matched "${spec}" from ${filename}`);
  }
}

/** Astro.fetchContent() */
export function fetchContent(spec: string, { namespace, filename }: GlobOptions): GlobResult {
  let code = '';
  const imports = new Set<string>();
  const importPaths = globSearch(spec, { filename });

  // gather imports
  importPaths.forEach((importPath, j) => {
    const id = `${namespace}_${j}`;
    imports.add(`import { __content as ${id} } from '${importPath}';`);

    // add URL if this appears within the /pages/ directory (probably can be improved)
    const fullPath = path.resolve(path.dirname(filename), importPath);
    if (fullPath.includes(`${path.sep}pages${path.sep}`)) {
      const url = importPath.replace(/^\./, '').replace(/\.md$/, '');
      imports.add(`${id}.url = '${url}';`);
    }
  });

  // generate replacement code
  code += `${namespace} = [${importPaths.map((_, j) => `${namespace}_${j}`).join(',')}];\n`;

  return { imports, code };
}