22
33const path = require ( 'path' )
44const Promise = require ( 'bluebird' )
5- const fs = require ( './lib/fs ' )
5+ const fs = require ( 'fs-extra ' )
66
77const cloneDeep = require ( 'lodash.clonedeep' )
88const browserify = require ( 'browserify' )
99const watchify = require ( 'watchify' )
1010
1111const debug = require ( 'debug' ) ( 'cypress:browserify' )
1212
13+ const typescriptExtensionRegex = / \. t s x ? $ /
14+ const errorTypes = {
15+ TYPESCRIPT_AND_TSIFY : 'TYPESCRIPT_AND_TSIFY' ,
16+ TYPESCRIPT_NONEXISTENT : 'TYPESCRIPT_NONEXISTENT' ,
17+ TYPESCRIPT_NOT_CONFIGURED : 'TYPESCRIPT_NOT_CONFIGURED' ,
18+ TYPESCRIPT_NOT_STRING : 'TYPESCRIPT_NOT_STRING' ,
19+ }
20+
1321const bundles = { }
1422
1523// by default, we transform JavaScript (including some proposal features),
@@ -60,7 +68,17 @@ const defaultOptions = {
6068 } ,
6169}
6270
63- const getBrowserifyOptions = ( entry , userBrowserifyOptions = { } , typescriptPath = null ) => {
71+ const throwError = ( { message, type } ) => {
72+ const prefix = 'Error running @cypress/browserify-preprocessor:\n\n'
73+
74+ const err = new Error ( `${ prefix } ${ message } ` )
75+
76+ if ( type ) err . type = type
77+
78+ throw err
79+ }
80+
81+ const getBrowserifyOptions = async ( entry , userBrowserifyOptions = { } , typescriptPath = null ) => {
6482 let browserifyOptions = cloneDeep ( defaultOptions . browserifyOptions )
6583
6684 // allow user to override default options
@@ -82,22 +100,38 @@ const getBrowserifyOptions = (entry, userBrowserifyOptions = {}, typescriptPath
82100 } )
83101
84102 if ( typescriptPath ) {
103+ if ( typeof typescriptPath !== 'string' ) {
104+ throwError ( {
105+ type : errorTypes . TYPESCRIPT_NOT_STRING ,
106+ message : `The 'typescript' option must be a string. You passed: ${ typescriptPath } ` ,
107+ } )
108+ }
109+
110+ const pathExists = await fs . pathExists ( typescriptPath )
111+
112+ if ( ! pathExists ) {
113+ throwError ( {
114+ type : errorTypes . TYPESCRIPT_NONEXISTENT ,
115+ message : `The 'typescript' option must be a valid path to your TypeScript installation. We could not find anything at the following path: ${ typescriptPath } ` ,
116+ } )
117+ }
118+
85119 const transform = browserifyOptions . transform
86120 const hasTsifyTransform = transform . some ( ( [ name ] ) => name . includes ( 'tsify' ) )
87121 const hastsifyPlugin = browserifyOptions . plugin . includes ( 'tsify' )
88122
89123 if ( hasTsifyTransform || hastsifyPlugin ) {
90124 const type = hasTsifyTransform ? 'transform' : 'plugin'
91125
92- throw new Error ( `Error running @cypress/browserify-preprocessor:
93-
94- It looks like you passed the 'typescript' option and also specified a browserify ${ type } for TypeScript. This may cause conflicts.
126+ throwError ( {
127+ type : errorTypes . TYPESCRIPT_AND_TSIFY ,
128+ message : ` It looks like you passed the 'typescript' option and also specified a browserify ${ type } for TypeScript. This may cause conflicts.
95129
96130Please do one of the following:
97131
981321) Pass in the 'typescript' option and omit the browserify ${ type } (Recommmended)
99- 2) Omit the 'typescript' option and continue to use your own browserify ${ type }
100- ` )
133+ 2) Omit the 'typescript' option and continue to use your own browserify ${ type } ` ,
134+ } )
101135 }
102136
103137 browserifyOptions . extensions . push ( '.ts' , '.tsx' )
@@ -135,7 +169,7 @@ const preprocessor = (options = {}) => {
135169 // when running in the GUI, it will likely get called multiple times
136170 // with the same filePath, as the user could re-run the tests, causing
137171 // the supported file and spec file to be requested again
138- return ( file ) => {
172+ return async ( file ) => {
139173 const filePath = file . filePath
140174
141175 debug ( 'get:' , filePath )
@@ -157,9 +191,18 @@ const preprocessor = (options = {}) => {
157191 debug ( 'input:' , filePath )
158192 debug ( 'output:' , outputPath )
159193
160- const browserifyOptions = getBrowserifyOptions ( filePath , options . browserifyOptions , options . typescript )
194+ const browserifyOptions = await getBrowserifyOptions ( filePath , options . browserifyOptions , options . typescript )
161195 const watchifyOptions = Object . assign ( { } , defaultOptions . watchifyOptions , options . watchifyOptions )
162196
197+ if ( ! options . typescript && typescriptExtensionRegex . test ( filePath ) ) {
198+ throwError ( {
199+ type : errorTypes . TYPESCRIPT_NOT_CONFIGURED ,
200+ message : `You are attempting to preprocess a TypeScript file, but do not have TypeScript configured. Pass the 'typescript' option to enable TypeScript support.
201+
202+ The file: ${ filePath } ` ,
203+ } )
204+ }
205+
163206 const bundler = browserify ( browserifyOptions )
164207
165208 if ( file . shouldWatch ) {
@@ -227,7 +270,7 @@ const preprocessor = (options = {}) => {
227270 } )
228271
229272 const bundlePromise = fs
230- . ensureDirAsync ( path . dirname ( outputPath ) )
273+ . ensureDir ( path . dirname ( outputPath ) )
231274 . then ( bundle )
232275
233276 // cache the bundle promise, so it can be returned if this function
@@ -253,6 +296,8 @@ const preprocessor = (options = {}) => {
253296// provide a clone of the default options
254297preprocessor . defaultOptions = JSON . parse ( JSON . stringify ( defaultOptions ) )
255298
299+ preprocessor . errorTypes = errorTypes
300+
256301if ( process . env . __TESTING__ ) {
257302 preprocessor . reset = ( ) => {
258303 for ( let filePath in bundles ) {
0 commit comments