@@ -4,8 +4,9 @@ const { readFileSync } = require('fs');
44const stackTrace = require ( 'stack-trace' ) ;
55const { SourceMapConsumer } = require ( 'source-map' ) ;
66const { error, info } = require ( '../../util' ) ;
7+ const outdent = require ( 'outdent' ) ;
78
8- module . exports = function ( env , params ) {
9+ module . exports = function prerender ( env , params ) {
910 params = params || { } ;
1011
1112 let entry = resolve ( env . dest , './ssr-build/ssr-bundle.js' ) ;
@@ -47,12 +48,26 @@ module.exports = function(env, params) {
4748 }
4849} ;
4950
51+ function getLines ( env , position ) {
52+ let sourcePath ;
53+ try {
54+ sourcePath = resolve ( env . src , position . source ) ;
55+ return readFileSync ( sourcePath , 'utf-8' ) . split ( '\n' ) ;
56+ } catch ( err ) {
57+ try {
58+ sourcePath = resolve ( env . cwd , position . source ) ;
59+ return readFileSync ( sourcePath , 'utf-8' ) . split ( '\n' ) ;
60+ } catch ( err ) {
61+ error ( `Unable to read file: ${ sourcePath } (${ position . source } )\n` ) ;
62+ }
63+ }
64+ }
65+
5066async function handlePrerenderError ( err , env , stack , entry ) {
5167 const errorMessage = err . toString ( ) ;
5268 const isReferenceError = errorMessage . startsWith ( 'ReferenceError' ) ;
5369 const methodName = stack . getMethodName ( ) ;
5470 const fileName = stack . getFileName ( ) . replace ( / \\ / g, '/' ) ;
55- let sourceCodeHighlight = '' ;
5671
5772 let position ;
5873
@@ -65,7 +80,9 @@ async function handlePrerenderError(err, env, stack, entry) {
6580 } ;
6681 } else {
6782 try {
68- const sourceMapContent = JSON . parse ( readFileSync ( `${ entry } .map` ) ) ;
83+ const sourceMapContent = JSON . parse (
84+ readFileSync ( `${ entry } .map` , 'utf-8' )
85+ ) ;
6986
7087 await SourceMapConsumer . with ( sourceMapContent , null , consumer => {
7188 position = consumer . originalPositionFor ( {
@@ -75,7 +92,6 @@ async function handlePrerenderError(err, env, stack, entry) {
7592 } ) ;
7693 } catch ( err ) {
7794 error ( `Unable to read sourcemap: ${ entry } .map` ) ;
78- return ;
7995 }
8096 }
8197
@@ -89,59 +105,6 @@ async function handlePrerenderError(err, env, stack, entry) {
89105 . replace ( / ^ ( .* ?\/ n o d e _ m o d u l e s \/ ( @ [ ^ / ] + \/ ) ? [ ^ / ] + ) ( \/ .* ) $ / , '$1' )
90106 ) ;
91107 info ( position . source ) ;
92-
93- let sourcePath ;
94- let sourceLines ;
95- try {
96- sourcePath = resolve ( env . src , position . source ) ;
97- sourceLines = readFileSync ( sourcePath , 'utf-8' ) . split ( '\n' ) ;
98- } catch ( err ) {
99- try {
100- sourcePath = resolve ( env . cwd , position . source ) ;
101- // sourcePath = require.resolve(position.source);
102- sourceLines = readFileSync ( sourcePath , 'utf-8' ) . split ( '\n' ) ;
103- } catch ( err ) {
104- error ( `Unable to read file: ${ sourcePath } (${ position . source } )\n` ) ;
105- return ;
106- }
107- }
108-
109- if ( sourceLines ) {
110- let lnrl = position . line . toString ( ) . length + 1 ;
111- sourceCodeHighlight +=
112- gray (
113- ( position . line - 2 || '' ) . toString ( ) . padStart ( lnrl ) +
114- ' | ' +
115- sourceLines [ position . line - 3 ] || ''
116- ) + '\n' ;
117- sourceCodeHighlight +=
118- gray (
119- ( position . line - 1 || '' ) . toString ( ) . padStart ( lnrl ) +
120- ' | ' +
121- sourceLines [ position . line - 2 ] || ''
122- ) + '\n' ;
123- sourceCodeHighlight +=
124- red ( position . line . toString ( ) . padStart ( lnrl ) ) +
125- gray ( ' | ' ) +
126- sourceLines [ position . line - 1 ] +
127- '\n' ;
128- sourceCodeHighlight +=
129- gray ( '| ' . padStart ( lnrl + 3 ) ) +
130- red ( '^' . padStart ( position . column + 1 ) ) +
131- '\n' ;
132- sourceCodeHighlight +=
133- gray (
134- ( position . line + 1 ) . toString ( ) . padStart ( lnrl ) +
135- ' | ' +
136- sourceLines [ position . line + 0 ] || ''
137- ) + '\n' ;
138- sourceCodeHighlight +=
139- gray (
140- ( position . line + 2 ) . toString ( ) . padStart ( lnrl ) +
141- ' | ' +
142- sourceLines [ position . line + 1 ] || ''
143- ) + '\n' ;
144- }
145108 } else {
146109 position = {
147110 source : stack . getFileName ( ) ,
@@ -150,37 +113,51 @@ async function handlePrerenderError(err, env, stack, entry) {
150113 } ;
151114 }
152115
153- process . stderr . write ( '\n' ) ;
154- process . stderr . write ( `[PrerenderError]: ${ red ( `${ errorMessage } \n` ) } ` ) ;
155- process . stderr . write (
156- ` --> ${ position . source } :${ position . line } :${
157- position . column
158- } (${ methodName || '<anonymous>' } )\n`
159- ) ;
160- process . stderr . write ( sourceCodeHighlight + '\n' ) ;
161- process . stderr . write ( red ( `${ err . stack } \n` ) ) ;
162-
163- process . stderr . write (
164- `This ${
165- isReferenceError ? 'is most likely' : 'could be'
166- } caused by using DOM or Web APIs.\n`
167- ) ;
168- process . stderr . write (
169- `Pre-render runs in node and has no access to globals available in browsers.\n`
170- ) ;
171- process . stderr . write (
172- `Consider wrapping code producing error in: 'if (typeof window !== "undefined") { ... }'\n`
173- ) ;
174-
175- if ( methodName === 'componentWillMount' ) {
176- process . stderr . write ( `or place logic in 'componentDidMount' method.\n` ) ;
116+ const sourceLines = getLines ( env , position ) ;
117+
118+ let sourceCodeHighlight = '' ;
119+ if ( sourceLines ) {
120+ const lnrl = position . line . toString ( ) . length + 2 ;
121+ const line = position . line ;
122+ const un = undefined ;
123+
124+ const pad = l =>
125+ ( l === undefined ? '' : ( line + l || '' ) + '' ) . padStart ( lnrl ) ;
126+
127+ sourceCodeHighlight = gray ( outdent `
128+ ${ pad ( - 2 ) } | ${ sourceLines [ line - 3 ] || '' }
129+ ${ pad ( - 1 ) } | ${ sourceLines [ line - 2 ] || '' }
130+ ${ pad ( - 0 ) } | ${ sourceLines [ line - 1 ] || '' }
131+ ${ pad ( un ) } | ${ red ( '^' . padStart ( position . column + 1 ) ) }
132+ ${ pad ( + 1 ) } | ${ sourceLines [ line + 0 ] || '' }
133+ ${ pad ( + 2 ) } | ${ sourceLines [ line + 1 ] || '' }
134+ ` ) ;
177135 }
178- process . stderr . write ( '\n' ) ;
179- process . stderr . write (
180- 'Alternatively use `preact build --no-prerender` to disable prerendering.\n'
181- ) ;
182- process . stderr . write (
183- 'See https://github.com/developit/preact-cli#pre-rendering for further information.'
184- ) ;
136+
137+ const stderr = process . stderr . write . bind ( process . stderr ) ;
138+
139+ stderr ( '\n' ) ;
140+ stderr ( outdent `
141+ [PrerenderError]: ${ red ( `${ errorMessage } ` ) }
142+ --> ${ position . source } :${ position . line } :${ position . column } (${ methodName ||
143+ '<anonymous>' } )
144+ ${ sourceCodeHighlight }
145+
146+ ${ red ( `${ err . stack } ` ) }
147+
148+ This ${
149+ isReferenceError ? 'is most likely' : 'could be'
150+ } caused by using DOM or Web APIs.
151+ Pre-render runs in node and has no access to globals available in browsers.
152+ Consider wrapping code producing error in: 'if (typeof window !== "undefined") { ... }\
153+ ${
154+ methodName === 'componentWillMount'
155+ ? `\nor place logic in 'componentDidMount' method.`
156+ : ''
157+ }
158+
159+ Alternatively use \`preact build --no-prerender\` to disable prerendering.
160+ See https://github.com/developit/preact-cli#pre-rendering for further information.
161+ ` ) ;
185162 process . exit ( 1 ) ;
186163}
0 commit comments