summaryrefslogtreecommitdiff
path: root/src/compiler/codegen/content.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/codegen/content.ts')
-rw-r--r--src/compiler/codegen/content.ts78
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 };
+}