next-serverless-loader.ts 4.7 KB
Newer Older
1
import { loader } from 'webpack'
2 3
import { join } from 'path'
import { parse } from 'querystring'
T
Tim Neutkens 已提交
4
import { BUILD_MANIFEST, REACT_LOADABLE_MANIFEST } from 'next-server/constants'
5
import { isDynamicRoute } from 'next-server/dist/lib/router/utils'
6
import { API_ROUTE } from '../../../lib/constants'
T
Tim Neutkens 已提交
7 8

export type ServerlessLoaderQuery = {
9 10 11 12 13 14 15 16
  page: string
  distDir: string
  absolutePagePath: string
  absoluteAppPath: string
  absoluteDocumentPath: string
  absoluteErrorPath: string
  assetPrefix: string
  ampBindInitData: boolean | string
T
Tim Neutkens 已提交
17
  generateEtags: string
18
  canonicalBase: string
T
Tim Neutkens 已提交
19 20
}

21
const nextServerlessLoader: loader.Loader = function() {
T
Tim Neutkens 已提交
22 23 24 25
  const {
    distDir,
    absolutePagePath,
    page,
26
    canonicalBase,
T
Tim Neutkens 已提交
27
    assetPrefix,
J
JJ Kasper 已提交
28
    ampBindInitData,
T
Tim Neutkens 已提交
29 30 31
    absoluteAppPath,
    absoluteDocumentPath,
    absoluteErrorPath,
32
    generateEtags,
33 34
  }: ServerlessLoaderQuery =
    typeof this.query === 'string' ? parse(this.query.substr(1)) : this.query
T
Tim Neutkens 已提交
35
  const buildManifest = join(distDir, BUILD_MANIFEST).replace(/\\/g, '/')
36 37 38 39
  const reactLoadableManifest = join(distDir, REACT_LOADABLE_MANIFEST).replace(
    /\\/g,
    '/'
  )
40

41
  if (page.match(API_ROUTE)) {
42 43 44 45 46 47 48 49 50
    return `
    ${
      isDynamicRoute(page)
        ? `
      import { getRouteMatcher } from 'next-server/dist/lib/router/utils/route-matcher';
      import { getRouteRegex } from 'next-server/dist/lib/router/utils/route-regex';
      `
        : ``
    }
51
      import { parse } from 'url'
52
      import { apiResolver } from 'next-server/dist/server/api-utils'
53

54 55 56
      export default (req, res) => {
        const params = ${
          isDynamicRoute(page)
57
            ? `getRouteMatcher(getRouteRegex('${page}'))(parse(req.url).pathname)`
58 59 60 61 62 63 64 65
            : `{}`
        }
        const resolver = require('${absolutePagePath}')
        apiResolver(req, res, params, resolver)
      }
    `
  } else {
    return `
T
Tim Neutkens 已提交
66 67 68
    import {parse} from 'url'
    import {renderToHTML} from 'next-server/dist/server/render';
    import {sendHTML} from 'next-server/dist/server/send-html';
J
Joe Haddad 已提交
69
    ${
70
      isDynamicRoute(page)
71
        ? `import {getRouteMatcher, getRouteRegex} from 'next-server/dist/lib/router/utils';`
J
Joe Haddad 已提交
72 73
        : ''
    }
T
Tim Neutkens 已提交
74 75 76 77 78
    import buildManifest from '${buildManifest}';
    import reactLoadableManifest from '${reactLoadableManifest}';
    import Document from '${absoluteDocumentPath}';
    import Error from '${absoluteErrorPath}';
    import App from '${absoluteAppPath}';
79 80
    import * as ComponentInfo from '${absolutePagePath}';
    const Component = ComponentInfo.default
J
JJ Kasper 已提交
81
    export default Component
82
    export const config = ComponentInfo['confi' + 'g'] || {}
J
JJ Kasper 已提交
83 84
    export const _app = App
    export async function renderReqToHTML(req, res, fromExport) {
T
Tim Neutkens 已提交
85 86 87 88 89
      const options = {
        App,
        Document,
        buildManifest,
        reactLoadableManifest,
90
        canonicalBase: "${canonicalBase}",
91
        buildId: "__NEXT_REPLACE__BUILD_ID__",
J
JJ Kasper 已提交
92
        assetPrefix: "${assetPrefix}",
93 94
        ampBindInitData: ${ampBindInitData === true ||
          ampBindInitData === 'true'}
T
Tim Neutkens 已提交
95 96
      }
      const parsedUrl = parse(req.url, true)
J
JJ Kasper 已提交
97 98 99
      const renderOpts = Object.assign(
        {
          Component,
100
          pageConfig: config,
J
JJ Kasper 已提交
101
          dataOnly: req.headers && (req.headers.accept || '').indexOf('application/amp.bind+json') !== -1,
102
          nextExport: fromExport
J
JJ Kasper 已提交
103 104 105
        },
        options,
      )
T
Tim Neutkens 已提交
106 107
      try {
        ${page === '/_error' ? `res.statusCode = 404` : ''}
J
Joe Haddad 已提交
108
        ${
109
          isDynamicRoute(page)
110
            ? `const params = fromExport ? {} : getRouteMatcher(getRouteRegex("${page}"))(parsedUrl.pathname) || {};`
J
Joe Haddad 已提交
111 112 113
            : `const params = {};`
        }
        const result = await renderToHTML(req, res, "${page}", Object.assign({}, parsedUrl.query, params), renderOpts)
J
JJ Kasper 已提交
114 115

        if (fromExport) return { html: result, renderOpts }
T
Tim Neutkens 已提交
116 117 118 119
        return result
      } catch (err) {
        if (err.code === 'ENOENT') {
          res.statusCode = 404
120
          const result = await renderToHTML(req, res, "/_error", parsedUrl.query, Object.assign({}, options, {
T
Tim Neutkens 已提交
121
            Component: Error
122
          }))
T
Tim Neutkens 已提交
123 124 125 126
          return result
        } else {
          console.error(err)
          res.statusCode = 500
127
          const result = await renderToHTML(req, res, "/_error", parsedUrl.query, Object.assign({}, options, {
T
Tim Neutkens 已提交
128 129
            Component: Error,
            err
130
          }))
T
Tim Neutkens 已提交
131 132 133 134
          return result
        }
      }
    }
135
    export async function render (req, res) {
T
Tim Neutkens 已提交
136
      try {
137
        const html = await renderReqToHTML(req, res)
T
Tim Neutkens 已提交
138 139 140 141 142 143 144 145
        sendHTML(req, res, html, {generateEtags: ${generateEtags}})
      } catch(err) {
        console.error(err)
        res.statusCode = 500
        res.end('Internal Server Error')
      }
    }
  `
146
  }
T
Tim Neutkens 已提交
147 148 149
}

export default nextServerlessLoader