aboutsummaryrefslogtreecommitdiff
path: root/source/import.ts
diff options
context:
space:
mode:
Diffstat (limited to 'source/import.ts')
-rw-r--r--source/import.ts104
1 files changed, 104 insertions, 0 deletions
diff --git a/source/import.ts b/source/import.ts
new file mode 100644
index 0000000..a9692bf
--- /dev/null
+++ b/source/import.ts
@@ -0,0 +1,104 @@
+
+
+
+/*** IMPORT ------------------------------------------- ***/
+
+import { dirname, join } from "@std/path";
+
+/*** UTILITY ------------------------------------------ ***/
+
+const { cwd, readFileSync } = Deno;
+const DYNAMIC_IMPORT_REGEX = /^#\s(DYNAMIC_IMPORTS)/gm;
+const ENCODING = "utf-8";
+const FILE_REGEX = /\w*(.graphql)/g;
+const IMPORT_REGEX = /^#\s(import)\s.*(.graphql")/gm;
+
+/*** EXPORT ------------------------------------------- ***/
+
+/**
+ * Reads a `.graphql` file and resolves its imports into a single SDL string.
+ *
+ * Two mechanisms are supported, and both can appear in the same file:
+ *
+ * 1. **Explicit imports** — lines like `# import "./user.graphql"` are replaced
+ * inline with the contents of the referenced file, resolved relative to the
+ * entry file.
+ * 2. **Dynamic imports** — if the file contains `# DYNAMIC_IMPORTS`, every
+ * `.graphql` file found one level below `<cwd>/schema/` is inlined at that
+ * marker. Useful for feature-folder layouts.
+ *
+ * Errors are logged and an empty string is returned so boot doesn't crash.
+ *
+ * @param path - Path to the entry `.graphql` file, relative to `Deno.cwd()`.
+ * @returns The fully-expanded schema string.
+ */
+export async function importQL(path: string): Promise<string> {
+ const SCHEMA_DIRECTORY = join(cwd(), "schema");
+
+ try {
+ const decoder = new TextDecoder(ENCODING);
+ const file = readFileSync(join(cwd(), String(path)));
+ const imports = decoder.decode(file).match(IMPORT_REGEX) || [];
+ const shouldTryDynamicallyImporting = decoder.decode(file).match(DYNAMIC_IMPORT_REGEX) ? true : false;
+ let parsedFile = decoder.decode(file);
+
+ /*** `import` statements in the supplied schema file
+ are parsed to dynamically bring in linked files. ***/
+
+ imports.map((imp: string) => {
+ const matchedFilename: null | Array<string> = imp.match(FILE_REGEX);
+
+ if (!matchedFilename || !matchedFilename.length || matchedFilename.length < 1)
+ return;
+
+ const filename = matchedFilename[0];
+ const importedFileDecoder = new TextDecoder(ENCODING);
+ const importedFile = Deno.readFileSync(join(dirname(String(path)), filename));
+ const decodedFile = importedFileDecoder.decode(importedFile);
+
+ parsedFile = parsedFile.replace(imp, decodedFile);
+ });
+
+ /*** With dynamic importing, we just look inside the `program`
+ directory to find `.graphql` files and automatically bring
+ them in, if `# DYNAMIC_IMPORTS` exists in `schema.graphql`. ***/
+
+ if (shouldTryDynamicallyImporting) {
+ const graphqlFiles = [];
+
+ for await (const dirEntry of Deno.readDir(SCHEMA_DIRECTORY)) {
+ const { isDirectory } = dirEntry;
+
+ if (isDirectory) {
+ const DIR = join(SCHEMA_DIRECTORY, dirEntry.name);
+
+ for await (const dirEntry of Deno.readDir(DIR)) {
+ const { isFile } = dirEntry;
+
+ if (isFile && dirEntry.name.match(FILE_REGEX))
+ graphqlFiles.push(join(DIR, dirEntry.name));
+ }
+ }
+ }
+
+ for (const file of graphqlFiles) {
+ const importedFileDecoder = new TextDecoder(ENCODING);
+ const importedFile = Deno.readFileSync(file);
+ const decodedFile = importedFileDecoder.decode(importedFile);
+ const insertPosition = parsedFile.indexOf("# DYNAMIC_IMPORTS");
+
+ if (insertPosition !== -1) {
+ parsedFile =
+ parsedFile.substring(0, insertPosition) +
+ decodedFile +
+ parsedFile.substring(insertPosition);
+ }
+ }
+ }
+
+ return parsedFile;
+ } catch(parseError) {
+ console.error(new Error(`error parsing file [${String(path)}]`), parseError);
+ return "";
+ }
+}