diff options
Diffstat (limited to 'src/compiler/codegen/content.ts')
-rw-r--r-- | src/compiler/codegen/content.ts | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/src/compiler/codegen/content.ts b/src/compiler/codegen/content.ts new file mode 100644 index 000000000..14542d533 --- /dev/null +++ b/src/compiler/codegen/content.ts @@ -0,0 +1,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("/…/astro/pages") ❌ + // …but this doesn’t: .glob("*.md").crawl("/…/astro/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}`); + } +} + +/** import.meta.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 }; +} |