diff --git a/packages/core/integration-tests/test/hmr.js b/packages/core/integration-tests/test/hmr.js index c873992fabd..f95df6c1c56 100644 --- a/packages/core/integration-tests/test/hmr.js +++ b/packages/core/integration-tests/test/hmr.js @@ -529,4 +529,56 @@ describe.skip('hmr', function() { await buildEnd; }); + + it('should watch new dependencies that cause errors', async function() { + await ncp( + path.join(__dirname, '/integration/elm-dep-error'), + path.join(__dirname, '/input') + ); + + b = bundler(path.join(__dirname, '/input/index.js'), { + watch: true, + hmr: true + }); + await b.bundle(); + + ws = new WebSocket('ws://localhost:' + b.options.hmrPort); + + const buildEnd = nextEvent(b, 'buildEnd'); + + await sleep(100); + fs.writeFile( + path.join(__dirname, '/input/src/Main.elm'), + ` +module Main exposing (main) + +import BrokenDep +import Html + +main = + Html.text "Hello, world!" + ` + ); + + let msg = JSON.parse(await nextEvent(ws, 'message')); + assert.equal(msg.type, 'error'); + + await sleep(100); + fs.writeFile( + path.join(__dirname, '/input/src/BrokenDep.elm'), + ` +module BrokenDep exposing (anError) + + +anError : String +anError = + "fixed" + ` + ); + + msg = JSON.parse(await nextEvent(ws, 'message')); + assert.equal(msg.type, 'error-resolved'); + + await buildEnd; + }); }); diff --git a/packages/core/integration-tests/test/integration/elm-dep-error/elm.json b/packages/core/integration-tests/test/integration/elm-dep-error/elm.json new file mode 100644 index 00000000000..dd41cae2a31 --- /dev/null +++ b/packages/core/integration-tests/test/integration/elm-dep-error/elm.json @@ -0,0 +1,24 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.0", + "dependencies": { + "direct": { + "elm/browser": "1.0.0", + "elm/core": "1.0.0", + "elm/html": "1.0.0" + }, + "indirect": { + "elm/json": "1.0.0", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.0" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} \ No newline at end of file diff --git a/packages/core/integration-tests/test/integration/elm-dep-error/index.js b/packages/core/integration-tests/test/integration/elm-dep-error/index.js new file mode 100644 index 00000000000..61906ed5a66 --- /dev/null +++ b/packages/core/integration-tests/test/integration/elm-dep-error/index.js @@ -0,0 +1,5 @@ +var local = require('./src/Main.elm'); + +module.exports = function () { + return local; +}; \ No newline at end of file diff --git a/packages/core/integration-tests/test/integration/elm-dep-error/src/BrokenDep.elm b/packages/core/integration-tests/test/integration/elm-dep-error/src/BrokenDep.elm new file mode 100644 index 00000000000..b52d49738bd --- /dev/null +++ b/packages/core/integration-tests/test/integration/elm-dep-error/src/BrokenDep.elm @@ -0,0 +1,8 @@ +module BrokenDep exposing (anError) + +{- This module causes a compiler error -} + + +anError : String +anError = + 2 diff --git a/packages/core/integration-tests/test/integration/elm-dep-error/src/Main.elm b/packages/core/integration-tests/test/integration/elm-dep-error/src/Main.elm new file mode 100644 index 00000000000..67393aaa654 --- /dev/null +++ b/packages/core/integration-tests/test/integration/elm-dep-error/src/Main.elm @@ -0,0 +1,7 @@ +module Main exposing (main) + +import Html + + +main = + Html.text "Hello, world!" diff --git a/packages/core/parcel-bundler/src/Bundler.js b/packages/core/parcel-bundler/src/Bundler.js index f8f135cb3bb..d791cb1f287 100644 --- a/packages/core/parcel-bundler/src/Bundler.js +++ b/packages/core/parcel-bundler/src/Bundler.js @@ -594,6 +594,13 @@ class Bundler extends EventEmitter { }) ); + // If there was a processing error, re-throw now that we've set up + // depdenency watchers. This keeps reloading working if there is an + // error in a dependency not directly handled by Parcel. + if (processed.error !== null) { + throw processed.error; + } + // Store resolved assets in their original order dependencies.forEach((dep, i) => { asset.dependencies.set(dep.name, dep); diff --git a/packages/core/parcel-bundler/src/Pipeline.js b/packages/core/parcel-bundler/src/Pipeline.js index b13edd8e4dc..0b016006fbc 100644 --- a/packages/core/parcel-bundler/src/Pipeline.js +++ b/packages/core/parcel-bundler/src/Pipeline.js @@ -1,5 +1,6 @@ const Parser = require('./Parser'); const path = require('path'); +const {errorUtils} = require('@parcel/utils'); /** * A Pipeline composes multiple Asset types together. @@ -17,10 +18,16 @@ class Pipeline { } let asset = this.parser.getAsset(path, options); - let generated = await this.processAsset(asset); + let error = null; let generatedMap = {}; - for (let rendition of generated) { - generatedMap[rendition.type] = rendition.value; + try { + let generated = await this.processAsset(asset); + for (let rendition of generated) { + generatedMap[rendition.type] = rendition.value; + } + } catch (err) { + error = errorUtils.errorToJson(err); + error.fileName = path; } return { diff --git a/packages/core/parcel-bundler/src/assets/ElmAsset.js b/packages/core/parcel-bundler/src/assets/ElmAsset.js index 7a2309b3a5f..5f81ebc1260 100644 --- a/packages/core/parcel-bundler/src/assets/ElmAsset.js +++ b/packages/core/parcel-bundler/src/assets/ElmAsset.js @@ -44,12 +44,7 @@ class ElmAsset extends Asset { options.optimize = true; } - let compiled = await this.elm.compileToString(this.name, options); - this.contents = compiled.toString(); - if (this.options.hmr) { - let {inject} = await localRequire('elm-hot', this.name); - this.contents = inject(this.contents); - } + this.elmOpts = options; } async collectDependencies() { @@ -76,6 +71,13 @@ class ElmAsset extends Asset { } async generate() { + let compiled = await this.elm.compileToString(this.name, this.elmOpts); + this.contents = compiled.toString(); + if (this.options.hmr) { + let {inject} = await localRequire('elm-hot', this.name); + this.contents = inject(this.contents); + } + let output = this.contents; if (this.options.minify) { @@ -129,6 +131,13 @@ class ElmAsset extends Asset { return result.code; } } + + generateErrorMessage(err) { + // The generated stack is not useful, but other code may + // expect it and try to print it, so make it an empty string. + err.stack = ''; + return err; + } } module.exports = ElmAsset;