Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Also recover from plugin errors during the initial build in watch mode #3219

Merged
merged 1 commit into from Nov 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/Module.ts
Expand Up @@ -272,8 +272,6 @@ export default class Module {
props.frame = getCodeFrame(this.originalCode, location.line, location.column);
}

props.watchFiles = Object.keys(this.graph.watchFiles);

error(props);
}

Expand Down
4 changes: 4 additions & 0 deletions src/rollup/index.ts
Expand Up @@ -199,6 +199,10 @@ export default async function rollup(rawInputOptions: GenericConfigObject): Prom
inputOptions.inlineDynamicImports as boolean
);
} catch (err) {
const watchFiles = Object.keys(graph.watchFiles);
if (watchFiles.length > 0) {
err.watchFiles = watchFiles;
}
await graph.pluginDriver.hookParallel('buildEnd', [err]);
throw err;
}
Expand Down
Expand Up @@ -20,6 +20,7 @@ module.exports = {
message:
'setAssetSource cannot be called in transform for caching reasons. Use emitFile with a source, or call setAssetSource in another hook.',
plugin: 'test-plugin',
pluginCode: 'INVALID_SETASSETSOURCE'
pluginCode: 'INVALID_SETASSETSOURCE',
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
@@ -1,3 +1,5 @@
const path = require('path');

module.exports = {
description: 'Throws if an emitted entry chunk cannot be resolved',
options: {
Expand All @@ -11,6 +13,7 @@ module.exports = {
},
error: {
code: 'UNRESOLVED_ENTRY',
message: 'Could not resolve entry module (not-found.js).'
message: 'Could not resolve entry module (not-found.js).',
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
Expand Up @@ -16,6 +16,7 @@ module.exports = {
message:
'Returning "dependencies" from the "transform" hook as done by plugin at position 1 is deprecated. The "this.addWatchFile" plugin context function should be used instead.',
plugin: 'at position 1',
pluginCode: 'DEPRECATED_FEATURE'
pluginCode: 'DEPRECATED_FEATURE',
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
@@ -1,7 +1,10 @@
const path = require('path');

module.exports = {
description: 'throws if a dynamic relative import is not found',
error: {
code: 'UNRESOLVED_IMPORT',
message: `Could not resolve './mod' from main.js`
message: `Could not resolve './mod' from main.js`,
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
5 changes: 4 additions & 1 deletion test/function/samples/emit-file/chunk-not-found/_config.js
@@ -1,3 +1,5 @@
const path = require('path');

module.exports = {
description: 'Throws if an emitted entry chunk cannot be resolved',
options: {
Expand All @@ -10,6 +12,7 @@ module.exports = {
},
error: {
code: 'UNRESOLVED_ENTRY',
message: 'Could not resolve entry module (not-found.js).'
message: 'Could not resolve entry module (not-found.js).',
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
Expand Up @@ -19,6 +19,7 @@ module.exports = {
message:
'setAssetSource cannot be called in transform for caching reasons. Use emitFile with a source, or call setAssetSource in another hook.',
plugin: 'test-plugin',
pluginCode: 'INVALID_SETASSETSOURCE'
pluginCode: 'INVALID_SETASSETSOURCE',
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
18 changes: 10 additions & 8 deletions test/function/samples/external-conflict/_config.js
@@ -1,22 +1,24 @@
const path = require('path');

module.exports = {
description: 'external paths from custom resolver remain external (#633)',
options: {
external: (_id, parent) => parent === 'dep',
plugins: [
{
resolveId (id, parent) {
if (id === 'dep')
return id;
resolveId(id, parent) {
if (id === 'dep') return id;
},
load (id) {
if (id === 'dep')
return `import 'dep'`;
load(id) {
if (id === 'dep') return `import 'dep'`;
}
}
]
},
error: {
code: "INVALID_EXTERNAL_ID",
message: "'dep' is imported as an external by dep, but is already an existing non-external module id."
code: 'INVALID_EXTERNAL_ID',
message:
"'dep' is imported as an external by dep, but is already an existing non-external module id.",
watchFiles: [path.resolve(__dirname, 'main.js'), 'dep']
}
};
5 changes: 4 additions & 1 deletion test/function/samples/load-returns-string-or-null/_config.js
@@ -1,3 +1,5 @@
const path = require('path');

module.exports = {
description: 'throws error if load returns something wacky',
options: {
Expand All @@ -12,6 +14,7 @@ module.exports = {
},
error: {
code: 'BAD_LOADER',
message: `Error loading main.js: plugin load hook should return a string, a { code, map } object, or nothing/null`
message: `Error loading main.js: plugin load hook should return a string, a { code, map } object, or nothing/null`,
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
5 changes: 4 additions & 1 deletion test/function/samples/manual-chunks-conflict/_config.js
@@ -1,3 +1,5 @@
const path = require('path');

module.exports = {
description: 'Throws for conflicts between manual chunks',
options: {
Expand All @@ -9,6 +11,7 @@ module.exports = {
},
error: {
code: 'INVALID_CHUNK',
message: `Cannot assign dep.js to the "dep2" chunk as it is already in the "dep1" chunk.`
message: `Cannot assign dep.js to the "dep2" chunk as it is already in the "dep1" chunk.`,
watchFiles: [path.resolve(__dirname, 'main.js'), path.resolve(__dirname, 'dep.js')]
}
};
5 changes: 4 additions & 1 deletion test/function/samples/no-relative-external/_config.js
@@ -1,7 +1,10 @@
const path = require('path');

module.exports = {
description: 'missing relative imports are an error, not a warning',
error: {
code: 'UNRESOLVED_IMPORT',
message: `Could not resolve './missing.js' from main.js`
message: `Could not resolve './missing.js' from main.js`,
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
5 changes: 4 additions & 1 deletion test/function/samples/paths-are-case-sensitive/_config.js
@@ -1,7 +1,10 @@
const path = require('path');

module.exports = {
description: 'insists on correct casing for imports',
error: {
code: 'UNRESOLVED_IMPORT',
message: `Could not resolve './foo.js' from main.js`
message: `Could not resolve './foo.js' from main.js`,
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
Expand Up @@ -18,6 +18,7 @@ module.exports = {
message: 'nope',
hook: 'transform',
id: path.resolve(__dirname, 'main.js'),
watchFiles: [path.resolve(__dirname, 'main.js')],
loc: {
file: path.resolve(__dirname, 'main.js'),
line: 1,
Expand Down
Expand Up @@ -23,6 +23,7 @@ module.exports = {
message: `Something happened 1`,
plugin: 'plugin1',
hook: 'transform',
id: path.resolve(__dirname, 'main.js')
id: path.resolve(__dirname, 'main.js'),
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
3 changes: 2 additions & 1 deletion test/function/samples/plugin-error/load/_config.js
Expand Up @@ -16,6 +16,7 @@ module.exports = {
code: 'PLUGIN_ERROR',
plugin: 'test',
message: `Could not load ${path.resolve(__dirname, 'main.js')}: nope`,
hook: 'load'
hook: 'load',
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
1 change: 1 addition & 0 deletions test/function/samples/plugin-error/transform/_config.js
Expand Up @@ -18,6 +18,7 @@ module.exports = {
message: 'nope',
hook: 'transform',
id: path.resolve(__dirname, 'main.js'),
watchFiles: [path.resolve(__dirname, 'main.js')],
pos: 22,
loc: {
file: path.resolve(__dirname, 'main.js'),
Expand Down
5 changes: 4 additions & 1 deletion test/function/samples/throws-not-found-module/_config.js
@@ -1,7 +1,10 @@
const path = require('path');

module.exports = {
description: 'throws error if module is not found',
error: {
code: 'UNRESOLVED_IMPORT',
message: `Could not resolve './mod' from main.js`
message: `Could not resolve './mod' from main.js`,
watchFiles: [path.resolve(__dirname, 'main.js')]
}
};
41 changes: 41 additions & 0 deletions test/watch/index.js
Expand Up @@ -363,6 +363,47 @@ describe('rollup.watch', () => {
});
});

it('recovers from a plugin error on initial build', () => {
let count = 0;
return sander
.copydir('test/watch/samples/basic')
.to('test/_tmp/input')
.then(() => {
const watcher = rollup.watch({
input: 'test/_tmp/input/main.js',
plugins: {
transform() {
if (count++ === 0) {
this.error('The first run failed, try again.');
}
}
},
output: {
file: 'test/_tmp/output/bundle.js',
format: 'cjs'
},
watch: { chokidar }
});

return sequence(watcher, [
'START',
'BUNDLE_START',
'ERROR',
() => {
assert.strictEqual(sander.existsSync('../_tmp/output/bundle.js'), false);
sander.writeFileSync('test/_tmp/input/main.js', 'export default 43;');
},
'START',
'BUNDLE_START',
'BUNDLE_END',
'END',
() => {
assert.strictEqual(run('../_tmp/output/bundle.js'), 43);
}
]);
});
});

it('recovers from an error even when erroring file was "renamed" (#38)', () => {
return sander
.copydir('test/watch/samples/basic')
Expand Down