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