diff --git a/lib/utils/query-runner.js b/lib/utils/query-runner.js index faced4d01459f..3cb657109c74f 100644 --- a/lib/utils/query-runner.js +++ b/lib/utils/query-runner.js @@ -4,6 +4,7 @@ const babylon = require(`babylon`) import fs from 'fs' import _ from 'lodash' const babel = require(`babel-core`) +import traverse from 'babel-traverse' import path from 'path' import { graphql } from 'graphql' import parseFilepath from 'parse-filepath' @@ -203,65 +204,6 @@ const babelPlugin = function ({ types: t }) { const query = chunks.join(``) console.time(`graphql query time`) const graphql = state.opts.graphql - _.each(state.opts.paths, (pathInfo) => { - // TODO - // - routeProvider thing + auto routes if component - // isn't a multiple route thingy. - // - Track which json file associated with which wrapper-component - // and delete old one when query changes. - // - Track ids of nodes associated with which queries - // and rerun query for a specific wrapper json file - // when an underying file changes. - // - Also track connections/ranges and mark their components - // as stale (just mark any query against connections stale if a file - // in that connection gets changed, so all ids in that connection.) - // - write plugin which just exports the src component - // instead of the fake HOC - // - Move all this into Gatsby (as seperate package within new Lerna - // structure). - // - schema for paths/pages and other internal info. Generate searchable sitemap page in dev. - // - Markdown schema - // * frontmatter — just skip deciding schema — config takes JOI schema - // for frontmatter though. - // * markdownAST - // * diff ways of parsing Markdown AST e.g. headings - // * bodyRaw - // * bodyHTML - // * timeToRead, etc. (computed/derived fields) - // * path — programatically alterable like now. - // * file info e.g. parseFilepath result. - graphql(query, pathInfo) - .catch(error => console.log(`graphql error`, error)) - .then(result => { - if (result.errors) { - console.log(`graphql errors`, result.errors) - } - // Combine the result with the pathInfo. - result.pathContext = pathInfo - // TODO see if can hash actual result not result + context - // which would combine some modules. Probably need to just - // insert the page context in child-routes. - // Also make page context more explicit? E.g. should never - // send component name to browser like we are now. - const resultHash = hashStr(JSON.stringify(result)) - - // Add result to page object. - const page = pagesDB().get(pathInfo.path) - page.data = result - page.resultHash = resultHash - pagesDB(pagesDB().set(page.path, page)) - - // Save result to file. - const resultJSON = JSON.stringify(result, null, 4) - let fileName = `${_.kebabCase(pathInfo.path)}.js` - if (fileName === `.js`) { - fileName = `index.js` - } - fs.writeFile(`${state.opts.directory}/.intermediate-representation/json/${resultHash}.json`, resultJSON) - }) - }) - console.log(`rewrote JSON for queries for ${state.opts.componentPath}`) - console.timeEnd(`graphql query time`) //path.parentPath.replaceWithSourceString(`require('fixme.json')`); } @@ -289,6 +231,31 @@ const q = queue(({ file, graphql, directory }, callback) => { console.log(`Failed to parse ${file}`) console.log(e) } + + // Get query for this file. + let query + traverse(ast, { + TemplateLiteral (path, state) { + if (path.parentPath.parentPath.parentPath.type !== `ExportNamedDeclaration`) { + return + } + const exportPath = path.parentPath.parentPath.parentPath + const name = _.get(exportPath, `node.declaration.declarations[0].id.name`) + if (name === `pageQuery`) { + const quasis = _.get(path, `node.quasis`, []) + const expressions = path.get(`expressions`) + const chunks = [] + quasis.forEach((quasi) => { + chunks.push(quasi.value.cooked) + const expr = expressions.shift() + if (expr) { + chunks.push(expr.scope.bindings[expr.node.name].path.get(`value`).parentPath.node.init.quasis[0].value.cooked) + } + }) + query = chunks.join(``) + } + }, + }) const absFile = path.resolve(file) // Get paths for this file. const paths = [] @@ -305,16 +272,43 @@ const q = queue(({ file, graphql, directory }, callback) => { } // Run queries for each page component. - try { - babel.transformFromAst(ast, ``, { - plugins: [[babelPlugin, { ...pathsInfo }]], + console.time(`graphql query time`) + Promise.all(paths.map((pathInfo) => { + return graphql(query, pathInfo) + .catch(error => console.log(`graphql error`, error)) + .then(result => { + if (result.errors) { + console.log(`graphql errors`, result.errors) + } + // Combine the result with the pathInfo. + const clonedResult = { ...result } + result.pathContext = pathInfo + const resultHash = hashStr(JSON.stringify(clonedResult)) + + // Add result to page object. + const page = pagesDB().get(pathInfo.path) + page.resultHash = resultHash + pagesDB(pagesDB().set(page.path, page)) + + // Save result to file. + const resultJSON = JSON.stringify(clonedResult, null, 4) + let fileName = `${_.kebabCase(pathInfo.path)}.js` + if (fileName === `.js`) { + fileName = `index.js` + } + fs.writeFileSync(`${directory}/.intermediate-representation/json/${resultHash}.json`, resultJSON) + + return null }) - } catch (e) { - console.log(`Failed to run queries for ${file}`) - console.log(e) - } - debouncedWriteChildRoutes() - callback() + })) + .then(() => { + console.log(`rewrote JSON for queries for ${absFile}`) + console.timeEnd(`graphql query time`) + // Write out new child-routes.js in the .intermediate-representation directory + // in the root of your site. + debouncedWriteChildRoutes() + callback() + }) }, 2) module.exports = (program, graphql, cb) => {