1 | /**
|
2 | * The handler, an instance of which is created for every instance of GQLBase.
|
3 | * The handler manages the fetching and decoding of files bearing the IDL
|
4 | * schema associated with the class represented by this instance of GQLBase.
|
5 | *
|
6 | * @class IDLFileHandler
|
7 | */
|
8 | export class IDLFileHandler {
|
9 | path: ?string;
|
10 |
|
11 | extension: ?string;
|
12 |
|
13 | /**
|
14 | * The IDLFileHandler checks the SCHEMA value returned by the class type
|
15 | * of the supplied instance. If the resulting value is a Symbol, then the
|
16 | * handler's responsibility is to find the file, load it from disk and
|
17 | * provide various means of using its contents; i.e. as a Buffer, a String
|
18 | * or wrapped in a SyntaxTree instance.
|
19 | *
|
20 | * @memberof IDLFileHandler
|
21 | * @method ⎆⠀constructor
|
22 | * @constructor
|
23 | *
|
24 | * @param {Function} Class a function or class definition that presumably
|
25 | * extends from GQLBase were it an instance.
|
26 | */
|
27 | constructor(Class: Function) {
|
28 | // $FlowFixMe
|
29 | const symbol = typeof Class.SCHEMA === 'symbol' && Class.SCHEMA || null;
|
30 | const pattern = /Symbol\(Path (.*?) Extension (.*?)\)/;
|
31 |
|
32 | if (symbol) {
|
33 | let symbolString = symbol.toString();
|
34 |
|
35 | if (symbol === Class.ADJACENT_FILE) {
|
36 | if (Class.module === module) {
|
37 | throw new Error(`
|
38 | The a static getter for 'module' on ${Class.name} must be present
|
39 | that returns the module object where the Class is defined. Try the
|
40 | following:
|
41 |
|
42 | // your ${Class.name}.js file
|
43 | import { GQLBase } from 'graphql-lattice'
|
44 |
|
45 | const ${Class.name}Module = module;
|
46 |
|
47 | class ${Class.name} extends GQLBase {
|
48 | ...
|
49 |
|
50 | static get module() {
|
51 | return ${Class.name}Module;
|
52 | }
|
53 | }
|
54 |
|
55 | `);
|
56 | }
|
57 |
|
58 | const filename = Class.module.filename;
|
59 | const extension = Path.extname(filename)
|
60 | const dir = Path.dirname(filename)
|
61 | const filefixed = Path.basename(filename, extension)
|
62 | const build = Path.resolve(Path.join(dir, `${filefixed}.graphql`))
|
63 |
|
64 | this.path = build;
|
65 | this.extension = '.graphql';
|
66 | }
|
67 | else if (pattern.test(symbolString)) {
|
68 | const parsed = pattern.exec(symbolString);
|
69 | const extension = parsed[2] || '.graphql'
|
70 | const dir = Path.dirname(parsed[1])
|
71 | const file = Path.basename(parsed[1], extension)
|
72 | const build = Path.resolve(Path.join(dir, `${file}${extension}`))
|
73 |
|
74 | this.path = build;
|
75 | this.extension = extension;
|
76 | }
|
77 | }
|
78 | else {
|
79 | this.path = this.extension = null;
|
80 | }
|
81 | }
|
82 |
|
83 | /**
|
84 | * Loads the calculated file determined by the decoding of the meaning of
|
85 | * the Symbol returned by the SCHEMA property of the instance supplied to
|
86 | * the IDLFileHandler upon creation.
|
87 | *
|
88 | * @instance
|
89 | * @memberof IDLFileHandler
|
90 | * @method ⌾⠀getFile
|
91 | *
|
92 | * @return {Buffer|null} returns the Buffer containing the file base IDL
|
93 | * schema or null if none was found or a direct string schema is returned
|
94 | * by the SCHEMA property
|
95 | */
|
96 | getFile(): Buffer {
|
97 | return fs.readFileSync(String(this.path));
|
98 | }
|
99 |
|
100 | /**
|
101 | * If getFile() returns a Buffer, this is the string representation of the
|
102 | * underlying file contents. As a means of validating the contents of the
|
103 | * file, the string contents are parsed into an AST and back to a string.
|
104 | *
|
105 | * @instance
|
106 | * @memberof IDLFileHandler
|
107 | * @method ⌾⠀getSchema
|
108 | *
|
109 | * @return {string|null} the string contents of the Buffer containing the
|
110 | * file based IDL schema.
|
111 | */
|
112 | getSchema(): ?string {
|
113 | if (!this.path) { return null; }
|
114 |
|
115 | const tree = this.getSyntaxTree();
|
116 |
|
117 | return tree.toString();
|
118 | }
|
119 |
|
120 | /**
|
121 | * If getFile() returns a Buffer, the string contents are passed to a new
|
122 | * instance of SyntaxTree which parses this into an AST for manipulation.
|
123 | *
|
124 | * @instance
|
125 | * @memberof IDLFileHandler
|
126 | * @method ⌾⠀getSyntaxTree
|
127 | *
|
128 | * @return {SyntaxTree|null} a SyntaxTree instance constructed from the IDL
|
129 | * schema contents loaded from disk. Null is returned if a calculated path
|
130 | * cannot be found; always occurs when SCHEMA returns a string.
|
131 | */
|
132 | getSyntaxTree(): SyntaxTree {
|
133 | const buffer = this.getFile();
|
134 | const tree = new SyntaxTree(buffer.toString());
|
135 |
|
136 | return tree;
|
137 | }
|
138 |
|
139 | /**
|
140 | * Returns the `constructor` name. If invoked as the context, or `this`,
|
141 | * object of the `toString` method of `Object`'s `prototype`, the resulting
|
142 | * value will be `[object MyClass]`, given an instance of `MyClass`
|
143 | *
|
144 | * @method ⌾⠀[Symbol.toStringTag]
|
145 | * @memberof IDLFileHandler
|
146 | *
|
147 | * @return {string} the name of the class this is an instance of
|
148 | * @ComputedType
|
149 | */
|
150 | get [Symbol.toStringTag]() { return this.constructor.name }
|
151 |
|
152 | /**
|
153 | * Applies the same logic as {@link #[Symbol.toStringTag]} but on a static
|
154 | * scale. So, if you perform `Object.prototype.toString.call(MyClass)`
|
155 | * the result would be `[object MyClass]`.
|
156 | *
|
157 | * @method ⌾⠀[Symbol.toStringTag]
|
158 | * @memberof IDLFileHandler
|
159 | * @static
|
160 | *
|
161 | * @return {string} the name of this class
|
162 | * @ComputedType
|
163 | */
|
164 | static get [Symbol.toStringTag]() { return this.name }
|
165 | }
|
166 |
|
167 | export default IDLFileHandler |
\ | No newline at end of file |