Browse Source

Merge pull request #1212 from rollup/gh-545

[WIP] [BREAKING] More consistent error logging, with code frames etc
gh-1187
Rich Harris 8 years ago
committed by GitHub
parent
commit
219f1f13da
  1. 65
      bin/src/handleError.js
  2. 47
      bin/src/logging.js
  3. 209
      bin/src/runRollup.js
  4. 44
      src/Bundle.js
  5. 77
      src/Module.js
  6. 12
      src/ast/nodes/CallExpression.js
  7. 23
      src/ast/nodes/shared/disallowIllegalReassignment.js
  8. 6
      src/finalisers/iife.js
  9. 6
      src/finalisers/umd.js
  10. 14
      src/rollup.js
  11. 5
      src/utils/collapseSourcemaps.js
  12. 9
      src/utils/defaults.js
  13. 11
      src/utils/getCodeFrame.js
  14. 11
      src/utils/getExportMode.js
  15. 25
      src/utils/transform.js
  16. 9
      src/utils/transformBundle.js
  17. 19
      test/function/cannot-call-external-namespace/_config.js
  18. 19
      test/function/cannot-call-internal-namespace/_config.js
  19. 16
      test/function/cannot-import-self/_config.js
  20. 5
      test/function/check-resolve-for-entry/_config.js
  21. 10
      test/function/custom-path-resolver-plural-b/_config.js
  22. 19
      test/function/default-not-reexported/_config.js
  23. 16
      test/function/double-default-export/_config.js
  24. 17
      test/function/double-named-export/_config.js
  25. 17
      test/function/double-named-reexport/_config.js
  26. 20
      test/function/duplicate-import-fails/_config.js
  27. 18
      test/function/duplicate-import-specifier-fails/_config.js
  28. 19
      test/function/export-not-at-top-level-fails/_config.js
  29. 5
      test/function/export-type-mismatch-b/_config.js
  30. 5
      test/function/export-type-mismatch-c/_config.js
  31. 5
      test/function/export-type-mismatch/_config.js
  32. 19
      test/function/import-not-at-top-level-fails/_config.js
  33. 2
      test/function/import-not-at-top-level-fails/main.js
  34. 19
      test/function/import-of-unexported-fails/_config.js
  35. 6
      test/function/load-returns-string-or-null/_config.js
  36. 19
      test/function/namespace-reassign-import-fails/_config.js
  37. 19
      test/function/namespace-update-import-fails/_config.js
  38. 5
      test/function/no-relative-external/_config.js
  39. 5
      test/function/paths-are-case-sensitive/_config.js
  40. 19
      test/function/reassign-import-fails/_config.js
  41. 20
      test/function/reassign-import-not-at-top-level-fails/_config.js
  42. 17
      test/function/reexport-missing-error/_config.js
  43. 9
      test/function/report-transform-error-file/_config.js
  44. 8
      test/function/reports-syntax-error-locations/_config.js
  45. 1
      test/function/reports-syntax-error-locations/main.js
  46. 5
      test/function/throws-not-found-module/_config.js
  47. 11
      test/function/throws-only-first-transform-bundle/_config.js
  48. 19
      test/function/throws-only-first-transform/_config.js
  49. 19
      test/function/update-expression-of-import-fails/_config.js
  50. 21
      test/test.js

65
bin/src/handleError.js

@ -1,65 +0,0 @@
import chalk from 'chalk';
function stderr ( msg ) {
console.error( msg ); // eslint-disable-line no-console
}
const handlers = {
MISSING_CONFIG: () => {
stderr( chalk.red( 'Config file must export an options object. See https://github.com/rollup/rollup/wiki/Command-Line-Interface#using-a-config-file' ) );
},
MISSING_EXTERNAL_CONFIG: err => {
stderr( chalk.red( `Could not resolve config file ${err.config}` ) );
},
MISSING_INPUT_OPTION: () => {
stderr( chalk.red( 'You must specify an --input (-i) option' ) );
},
MISSING_OUTPUT_OPTION: () => {
stderr( chalk.red( 'You must specify an --output (-o) option when creating a file with a sourcemap' ) );
},
MISSING_NAME: () => {
stderr( chalk.red( 'You must supply a name for UMD exports (e.g. `--name myModule`)' ) );
},
PARSE_ERROR: err => {
stderr( chalk.red( `Error parsing ${err.file}: ${err.message}` ) );
},
ONE_AT_A_TIME: () => {
stderr( chalk.red( 'rollup can only bundle one file at a time' ) );
},
DUPLICATE_IMPORT_OPTIONS: () => {
stderr( chalk.red( 'use --input, or pass input path as argument' ) );
},
ROLLUP_WATCH_NOT_INSTALLED: () => {
stderr( chalk.red( 'rollup --watch depends on the rollup-watch package, which could not be found. Install it with ' ) + chalk.cyan( 'npm install -D rollup-watch' ) );
},
WATCHER_MISSING_INPUT_OR_OUTPUT: () => {
stderr( chalk.red( 'must specify --input and --output when using rollup --watch' ) );
}
};
export default function handleError ( err, recover ) {
const handler = handlers[ err && err.code ];
if ( handler ) {
handler( err );
} else {
stderr( chalk.red( err.message || err ) );
if ( err.stack ) {
stderr( chalk.grey( err.stack ) );
}
}
stderr( `Type ${chalk.cyan( 'rollup --help' )} for help, or visit https://github.com/rollup/rollup/wiki` );
if ( !recover ) process.exit( 1 );
}

47
bin/src/logging.js

@ -0,0 +1,47 @@
import chalk from 'chalk';
import relativeId from '../../src/utils/relativeId.js';
if ( !process.stderr.isTTY ) chalk.enabled = false;
const warnSymbol = process.stderr.isTTY ? `⚠️ ` : `Warning: `;
const errorSymbol = process.stderr.isTTY ? `🚨 ` : `Error: `;
// log to stderr to keep `rollup main.js > bundle.js` from breaking
export const stderr = console.error.bind( console ); // eslint-disable-line no-console
export function handleWarning ( warning ) {
stderr( `${warnSymbol}${chalk.bold( warning.message )}` );
if ( warning.url ) {
stderr( chalk.cyan( warning.url ) );
}
if ( warning.loc ) {
stderr( `${relativeId( warning.loc.file )} (${warning.loc.line}:${warning.loc.column})` );
}
if ( warning.frame ) {
stderr( chalk.dim( warning.frame ) );
}
stderr( '' );
}
export function handleError ( err, recover ) {
stderr( `${errorSymbol}${chalk.bold( err.message )}` );
if ( err.url ) {
stderr( chalk.cyan( err.url ) );
}
if ( err.loc ) {
stderr( `${relativeId( err.loc.file )} (${err.loc.line}:${err.loc.column})` );
}
if ( err.frame ) {
stderr( chalk.dim( err.frame ) );
}
stderr( '' );
if ( !recover ) process.exit( 1 );
}

209
bin/src/runRollup.js

@ -2,27 +2,26 @@ import { realpathSync } from 'fs';
import * as rollup from 'rollup';
import relative from 'require-relative';
import chalk from 'chalk';
import handleError from './handleError';
import relativeId from '../../src/utils/relativeId.js';
import { handleWarning, handleError, stderr } from './logging.js';
import SOURCEMAPPING_URL from './sourceMappingUrl.js';
import { install as installSourcemapSupport } from 'source-map-support';
installSourcemapSupport();
if ( !process.stderr.isTTY ) chalk.enabled = false;
const warnSymbol = process.stderr.isTTY ? `⚠️ ` : `Warning: `;
// stderr to stderr to keep `rollup main.js > bundle.js` from breaking
const stderr = console.error.bind( console ); // eslint-disable-line no-console
export default function runRollup ( command ) {
if ( command._.length > 1 ) {
handleError({ code: 'ONE_AT_A_TIME' });
handleError({
code: 'ONE_AT_A_TIME',
message: 'rollup can only bundle one file at a time'
});
}
if ( command._.length === 1 ) {
if ( command.input ) {
handleError({ code: 'DUPLICATE_IMPORT_OPTIONS' });
handleError({
code: 'DUPLICATE_IMPORT_OPTIONS',
message: 'use --input, or pass input path as argument'
});
}
command.input = command._[0];
@ -51,7 +50,10 @@ export default function runRollup ( command ) {
config = relative.resolve( pkgName, process.cwd() );
} catch ( err ) {
if ( err.code === 'MODULE_NOT_FOUND' ) {
handleError({ code: 'MISSING_EXTERNAL_CONFIG', config });
handleError({
code: 'MISSING_EXTERNAL_CONFIG',
message: `Could not resolve config file ${config}`
});
}
throw err;
@ -64,37 +66,38 @@ export default function runRollup ( command ) {
rollup.rollup({
entry: config,
onwarn: message => {
if ( message.code === 'UNRESOLVED_IMPORT' ) return;
stderr( message.toString() );
onwarn: warning => {
if ( warning.code === 'UNRESOLVED_IMPORT' ) return;
handleWarning( warning );
}
}).then( bundle => {
const { code } = bundle.generate({
format: 'cjs'
});
})
.then( bundle => {
const { code } = bundle.generate({
format: 'cjs'
});
// temporarily override require
const defaultLoader = require.extensions[ '.js' ];
require.extensions[ '.js' ] = ( m, filename ) => {
if ( filename === config ) {
m._compile( code, filename );
} else {
defaultLoader( m, filename );
}
};
// temporarily override require
const defaultLoader = require.extensions[ '.js' ];
require.extensions[ '.js' ] = ( m, filename ) => {
if ( filename === config ) {
m._compile( code, filename );
} else {
defaultLoader( m, filename );
}
};
try {
const options = require( config );
if ( Object.keys( options ).length === 0 ) {
handleError({ code: 'MISSING_CONFIG' });
handleError({
code: 'MISSING_CONFIG',
message: 'Config file must export an options object',
url: 'https://github.com/rollup/rollup/wiki/Command-Line-Interface#using-a-config-file'
});
}
execute( options, command );
require.extensions[ '.js' ] = defaultLoader;
} catch ( err ) {
handleError( err );
}
})
.catch( stderr );
})
.catch( handleError );
} else {
execute( {}, command );
}
@ -157,21 +160,7 @@ function execute ( options, command ) {
if ( seen.has( str ) ) return;
seen.add( str );
stderr( `${warnSymbol}${chalk.bold( warning.message )}` );
if ( warning.url ) {
stderr( chalk.cyan( warning.url ) );
}
if ( warning.loc ) {
stderr( `${relativeId( warning.loc.file )} (${warning.loc.line}:${warning.loc.column})` );
}
if ( warning.frame ) {
stderr( chalk.dim( warning.frame ) );
}
stderr( '' );
handleWarning( warning );
};
}
@ -184,50 +173,52 @@ function execute ( options, command ) {
}
});
try {
if ( command.watch ) {
if ( !options.entry || ( !options.dest && !options.targets ) ) {
handleError({ code: 'WATCHER_MISSING_INPUT_OR_OUTPUT' });
}
if ( command.watch ) {
if ( !options.entry || ( !options.dest && !options.targets ) ) {
handleError({
code: 'WATCHER_MISSING_INPUT_OR_OUTPUT',
message: 'must specify --input and --output when using rollup --watch'
});
}
try {
const watch = relative( 'rollup-watch', process.cwd() );
const watcher = watch( rollup, options );
try {
const watch = relative( 'rollup-watch', process.cwd() );
const watcher = watch( rollup, options );
watcher.on( 'event', event => {
switch ( event.code ) {
case 'STARTING': // TODO this isn't emitted by newer versions of rollup-watch
stderr( 'checking rollup-watch version...' );
break;
watcher.on( 'event', event => {
switch ( event.code ) {
case 'STARTING': // TODO this isn't emitted by newer versions of rollup-watch
stderr( 'checking rollup-watch version...' );
break;
case 'BUILD_START':
stderr( 'bundling...' );
break;
case 'BUILD_START':
stderr( 'bundling...' );
break;
case 'BUILD_END':
stderr( 'bundled in ' + event.duration + 'ms. Watching for changes...' );
break;
case 'BUILD_END':
stderr( 'bundled in ' + event.duration + 'ms. Watching for changes...' );
break;
case 'ERROR':
handleError( event.error, true );
break;
case 'ERROR':
handleError( event.error, true );
break;
default:
stderr( 'unknown event', event );
}
});
} catch ( err ) {
if ( err.code === 'MODULE_NOT_FOUND' ) {
err.code = 'ROLLUP_WATCH_NOT_INSTALLED';
default:
stderr( 'unknown event', event );
}
handleError( err );
});
} catch ( err ) {
if ( err.code === 'MODULE_NOT_FOUND' ) {
handleError({
code: 'ROLLUP_WATCH_NOT_INSTALLED',
message: 'rollup --watch depends on the rollup-watch package, which could not be found. Install it with npm install -D rollup-watch'
});
}
} else {
bundle( options ).catch( handleError );
handleError( err );
}
} catch ( err ) {
handleError( err );
} else {
bundle( options ).catch( handleError );
}
}
@ -244,34 +235,42 @@ function assign ( target, source ) {
function bundle ( options ) {
if ( !options.entry ) {
handleError({ code: 'MISSING_INPUT_OPTION' });
handleError({
code: 'MISSING_INPUT_OPTION',
message: 'You must specify an --input (-i) option'
});
}
return rollup.rollup( options ).then( bundle => {
if ( options.dest ) {
return bundle.write( options );
}
return rollup.rollup( options )
.then( bundle => {
if ( options.dest ) {
return bundle.write( options );
}
if ( options.targets ) {
let result = null;
if ( options.targets ) {
let result = null;
options.targets.forEach( target => {
result = bundle.write( assign( clone( options ), target ) );
});
options.targets.forEach( target => {
result = bundle.write( assign( clone( options ), target ) );
});
return result;
}
return result;
}
if ( options.sourceMap && options.sourceMap !== 'inline' ) {
handleError({ code: 'MISSING_OUTPUT_OPTION' });
}
if ( options.sourceMap && options.sourceMap !== 'inline' ) {
handleError({
code: 'MISSING_OUTPUT_OPTION',
message: 'You must specify an --output (-o) option when creating a file with a sourcemap'
});
}
let { code, map } = bundle.generate( options );
let { code, map } = bundle.generate( options );
if ( options.sourceMap === 'inline' ) {
code += `\n//# ${SOURCEMAPPING_URL}=${map.toUrl()}\n`;
}
if ( options.sourceMap === 'inline' ) {
code += `\n//# ${SOURCEMAPPING_URL}=${map.toUrl()}\n`;
}
process.stdout.write( code );
});
process.stdout.write( code );
})
.catch( handleError );
}

44
src/Bundle.js

@ -17,6 +17,7 @@ import transformBundle from './utils/transformBundle.js';
import collapseSourcemaps from './utils/collapseSourcemaps.js';
import callIfFunction from './utils/callIfFunction.js';
import relativeId from './utils/relativeId.js';
import error from './utils/error.js';
import { dirname, isRelative, isAbsolute, normalize, relative, resolve } from './utils/path.js';
import BundleScope from './ast/scopes/BundleScope.js';
@ -106,7 +107,13 @@ export default class Bundle {
// of the entry module's dependencies
return this.resolveId( this.entry, undefined )
.then( id => {
if ( id == null ) throw new Error( `Could not resolve entry (${this.entry})` );
if ( id == null ) {
error({
code: 'UNRESOLVED_ENTRY',
message: `Could not resolve entry (${this.entry})`
});
}
this.entryId = id;
return this.fetchModule( id, undefined );
})
@ -268,7 +275,11 @@ export default class Bundle {
if ( typeof source === 'string' ) return source;
if ( source && typeof source === 'object' && source.code ) return source;
throw new Error( `Error loading ${id}: load hook should return a string, a { code, map } object, or nothing/null` );
// TODO report which plugin failed
error({
code: 'BAD_LOADER',
message: `Error loading ${relativeId( id )}: plugin load hook should return a string, a { code, map } object, or nothing/null`
});
})
.then( source => {
if ( typeof source === 'string' ) {
@ -337,7 +348,12 @@ export default class Bundle {
let isExternal = this.isExternal( externalId );
if ( !resolvedId && !isExternal ) {
if ( isRelative( source ) ) throw new Error( `Could not resolve '${source}' from ${module.id}` );
if ( isRelative( source ) ) {
error({
code: 'UNRESOLVED_IMPORT',
message: `Could not resolve '${source}' from ${relativeId( module.id )}`
});
}
this.warn({
code: 'UNRESOLVED_IMPORT',
@ -367,7 +383,20 @@ export default class Bundle {
});
} else {
if ( resolvedId === module.id ) {
throw new Error( `A module cannot import itself (${resolvedId})` );
// need to find the actual import declaration, so we can provide
// a useful error message. Bit hoop-jumpy but what can you do
const name = Object.keys( module.imports )
.find( name => {
const declaration = module.imports[ name ];
return declaration.source === source;
});
const declaration = module.imports[ name ].specifier.parent;
module.error({
code: 'CANNOT_IMPORT_SELF',
message: `A module cannot import itself`
}, declaration.start );
}
module.resolvedIds[ source ] = resolvedId;
@ -445,7 +474,12 @@ export default class Bundle {
const indentString = getIndentString( magicString, options );
const finalise = finalisers[ options.format ];
if ( !finalise ) throw new Error( `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` );
if ( !finalise ) {
error({
code: 'INVALID_OPTION',
message: `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}`
});
}
timeStart( 'render format' );

77
src/Module.js

@ -15,25 +15,27 @@ import enhance from './ast/enhance.js';
import clone from './ast/clone.js';
import ModuleScope from './ast/scopes/ModuleScope.js';
function tryParse ( code, comments, acornOptions, id ) {
function tryParse ( module, acornOptions ) {
try {
return parse( code, assign({
return parse( module.code, assign({
ecmaVersion: 8,
sourceType: 'module',
onComment: ( block, text, start, end ) => comments.push({ block, text, start, end }),
onComment: ( block, text, start, end ) => module.comments.push({ block, text, start, end }),
preserveParens: false
}, acornOptions ));
} catch ( err ) {
err.code = 'PARSE_ERROR';
err.file = id; // see above - not necessarily true, but true enough
err.message += ` in ${id}`;
throw err;
module.error({
code: 'PARSE_ERROR',
message: err.message.replace( / \(\d+:\d+\)$/, '' )
}, err.pos );
}
}
export default class Module {
constructor ({ id, code, originalCode, originalSourceMap, ast, sourceMapChain, resolvedIds, bundle }) {
this.code = code;
this.id = id;
this.bundle = bundle;
this.originalCode = originalCode;
this.originalSourceMap = originalSourceMap;
this.sourceMapChain = sourceMapChain;
@ -48,14 +50,12 @@ export default class Module {
this.ast = clone( ast );
this.astClone = ast;
} else {
this.ast = tryParse( code, this.comments, bundle.acornOptions, id ); // TODO what happens to comments if AST is provided?
this.ast = tryParse( this, bundle.acornOptions ); // TODO what happens to comments if AST is provided?
this.astClone = clone( this.ast );
}
timeEnd( 'ast' );
this.bundle = bundle;
this.id = id;
this.excludeFromSourcemap = /\0/.test( id );
this.context = bundle.getModuleContext( id );
@ -121,7 +121,10 @@ export default class Module {
const name = specifier.exported.name;
if ( this.exports[ name ] || this.reexports[ name ] ) {
throw new Error( `A module cannot have multiple exports with the same name ('${name}')` );
this.error({
code: 'DUPLICATE_EXPORT',
message: `A module cannot have multiple exports with the same name ('${name}')`
}, specifier.start );
}
this.reexports[ name ] = {
@ -141,8 +144,10 @@ export default class Module {
const identifier = ( node.declaration.id && node.declaration.id.name ) || node.declaration.name;
if ( this.exports.default ) {
// TODO indicate location
throw new Error( 'A module can only have one default export' );
this.error({
code: 'DUPLICATE_EXPORT',
message: `A module can only have one default export`
}, node.start );
}
this.exports.default = {
@ -182,7 +187,10 @@ export default class Module {
const exportedName = specifier.exported.name;
if ( this.exports[ exportedName ] || this.reexports[ exportedName ] ) {
throw new Error( `A module cannot have multiple exports with the same name ('${exportedName}')` );
this.error({
code: 'DUPLICATE_EXPORT',
message: `A module cannot have multiple exports with the same name ('${exportedName}')`
}, specifier.start );
}
this.exports[ exportedName ] = { localName };
@ -207,10 +215,10 @@ export default class Module {
const localName = specifier.local.name;
if ( this.imports[ localName ] ) {
const err = new Error( `Duplicated import '${localName}'` );
err.file = this.id;
err.loc = locate( this.code, specifier.start, { offsetLine: 1 });
throw err;
this.error({
code: 'DUPLICATE_IMPORT',
message: `Duplicated import '${localName}'`
}, specifier.start );
}
const isDefault = specifier.type === 'ImportDefaultSpecifier';
@ -282,6 +290,19 @@ export default class Module {
// }
}
error ( props, pos ) {
if ( pos !== undefined ) {
props.pos = pos;
const { line, column } = locate( this.code, pos, { offsetLine: 1 }); // TODO trace sourcemaps
props.loc = { file: this.id, line, column };
props.frame = getCodeFrame( this.code, line, column );
}
error( props );
}
findParent () {
// TODO what does it mean if we're here?
return null;
@ -372,11 +393,11 @@ export default class Module {
const declaration = otherModule.traceExport( importDeclaration.name );
if ( !declaration ) {
error({
message: `'${importDeclaration.name}' is not exported by ${relativeId( otherModule.id )} (imported by ${relativeId( this.id )}). For help fixing this error see https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module`,
file: this.id,
loc: locate( this.code, importDeclaration.specifier.start, { offsetLine: 1 })
});
this.error({
code: 'MISSING_EXPORT',
message: `'${importDeclaration.name}' is not exported by ${relativeId( otherModule.id )}`,
url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module`
}, importDeclaration.specifier.start );
}
return declaration;
@ -392,11 +413,11 @@ export default class Module {
const declaration = reexportDeclaration.module.traceExport( reexportDeclaration.localName );
if ( !declaration ) {
error({
message: `'${reexportDeclaration.localName}' is not exported by '${reexportDeclaration.module.id}' (imported by '${this.id}')`,
file: this.id,
loc: locate( this.code, reexportDeclaration.start, { offsetLine: 1 })
});
this.error({
code: 'MISSING_EXPORT',
message: `'${reexportDeclaration.localName}' is not exported by ${relativeId( reexportDeclaration.module.id )}`,
url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module`
}, reexportDeclaration.start );
}
return declaration;

12
src/ast/nodes/CallExpression.js

@ -1,5 +1,3 @@
import { locate } from 'locate-character';
import error from '../../utils/error.js';
import Node from '../Node.js';
import isProgramLevel from '../utils/isProgramLevel.js';
import callHasEffects from './shared/callHasEffects.js';
@ -10,12 +8,10 @@ export default class CallExpression extends Node {
const declaration = scope.findDeclaration( this.callee.name );
if ( declaration.isNamespace ) {
error({
message: `Cannot call a namespace ('${this.callee.name}')`,
file: this.module.id,
pos: this.start,
loc: locate( this.module.code, this.start, { offsetLine: 1 })
});
this.module.error({
code: 'CANNOT_CALL_NAMESPACE',
message: `Cannot call a namespace ('${this.callee.name}')`
}, this.start );
}
if ( this.callee.name === 'eval' && declaration.isGlobal ) {

23
src/ast/nodes/shared/disallowIllegalReassignment.js

@ -1,28 +1,21 @@
import { locate } from 'locate-character';
import error from '../../../utils/error.js';
// TODO tidy this up a bit (e.g. they can both use node.module.imports)
export default function disallowIllegalReassignment ( scope, node ) {
if ( node.type === 'MemberExpression' && node.object.type === 'Identifier' ) {
const declaration = scope.findDeclaration( node.object.name );
if ( declaration.isNamespace ) {
error({
message: `Illegal reassignment to import '${node.object.name}'`,
file: node.module.id,
pos: node.start,
loc: locate( node.module.code, node.start, { offsetLine: 1 })
});
node.module.error({
code: 'ILLEGAL_NAMESPACE_REASSIGNMENT',
message: `Illegal reassignment to import '${node.object.name}'`
}, node.start );
}
}
else if ( node.type === 'Identifier' ) {
if ( node.module.imports[ node.name ] && !scope.contains( node.name ) ) {
error({
message: `Illegal reassignment to import '${node.name}'`,
file: node.module.id,
pos: node.start,
loc: locate( node.module.code, node.start, { offsetLine: 1 })
});
node.module.error({
code: 'ILLEGAL_REASSIGNMENT',
message: `Illegal reassignment to import '${node.name}'`
}, node.start );
}
}
}

6
src/finalisers/iife.js

@ -1,5 +1,6 @@
import { blank } from '../utils/object.js';
import { getName } from '../utils/map-helpers.js';
import error from '../utils/error.js';
import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
import getGlobalNameMaker from './shared/getGlobalNameMaker.js';
@ -36,7 +37,10 @@ export default function iife ( bundle, magicString, { exportMode, indentString,
const args = bundle.externalModules.map( getName );
if ( exportMode !== 'none' && !name ) {
throw new Error( 'You must supply options.moduleName for IIFE bundles' );
error({
code: 'INVALID_OPTION',
message: `You must supply options.moduleName for IIFE bundles`
});
}
if ( exportMode === 'named' ) {

6
src/finalisers/umd.js

@ -1,5 +1,6 @@
import { blank } from '../utils/object.js';
import { getName, quotePath, req } from '../utils/map-helpers.js';
import error from '../utils/error.js';
import getInteropBlock from './shared/getInteropBlock.js';
import getExportBlock from './shared/getExportBlock.js';
import getGlobalNameMaker from './shared/getGlobalNameMaker.js';
@ -28,7 +29,10 @@ const wrapperOutro = '\n\n})));';
export default function umd ( bundle, magicString, { exportMode, indentString, intro, outro }, options ) {
if ( exportMode !== 'none' && !options.moduleName ) {
throw new Error( 'You must supply options.moduleName for UMD bundles' );
error({
code: 'INVALID_OPTION',
message: 'You must supply options.moduleName for UMD bundles'
});
}
warnOnBuiltins( bundle );

14
src/rollup.js

@ -4,6 +4,7 @@ import { writeFile } from './utils/fs.js';
import { assign, keys } from './utils/object.js';
import { mapSequence } from './utils/promise.js';
import validateKeys from './utils/validateKeys.js';
import error from './utils/error.js';
import { SOURCEMAPPING_URL } from './utils/sourceMappingURL.js';
import Bundle from './Bundle.js';
@ -50,15 +51,15 @@ function checkOptions ( options ) {
return new Error( 'The `transform`, `load`, `resolveId` and `resolveExternal` options are deprecated in favour of a unified plugin API. See https://github.com/rollup/rollup/wiki/Plugins for details' );
}
const error = validateKeys( keys(options), ALLOWED_KEYS );
if ( error ) return error;
const err = validateKeys( keys(options), ALLOWED_KEYS );
if ( err ) return err;
return null;
}
export function rollup ( options ) {
const error = checkOptions ( options );
if ( error ) return Promise.reject( error );
const err = checkOptions ( options );
if ( err ) return Promise.reject( err );
const bundle = new Bundle( options );
@ -105,7 +106,10 @@ export function rollup ( options ) {
generate,
write: options => {
if ( !options || !options.dest ) {
throw new Error( 'You must supply options.dest to bundle.write' );
error({
code: 'MISSING_OPTION',
message: 'You must supply options.dest to bundle.write'
});
}
const dest = options.dest;

5
src/utils/collapseSourcemaps.js

@ -1,4 +1,5 @@
import { encode } from 'sourcemap-codec';
import error from './error.js';
import { dirname, relative, resolve } from './path.js';
class Source {
@ -51,7 +52,9 @@ class Link {
} else if ( sourcesContent[ sourceIndex ] == null ) {
sourcesContent[ sourceIndex ] = traced.source.content;
} else if ( traced.source.content != null && sourcesContent[ sourceIndex ] !== traced.source.content ) {
throw new Error( `Multiple conflicting contents for sourcemap source ${source.filename}` );
error({
message: `Multiple conflicting contents for sourcemap source ${source.filename}`
});
}
segment[1] = sourceIndex;

9
src/utils/defaults.js

@ -1,6 +1,7 @@
import { lstatSync, readdirSync, readFileSync, realpathSync } from './fs.js'; // eslint-disable-line
import { basename, dirname, isAbsolute, resolve } from './path.js';
import { blank } from './object.js';
import error from './error.js';
export function load ( id ) {
return readFileSync( id, 'utf-8' );
@ -27,7 +28,13 @@ function addJsExtensionIfNecessary ( file ) {
}
export function resolveId ( importee, importer ) {
if ( typeof process === 'undefined' ) throw new Error( `It looks like you're using Rollup in a non-Node.js environment. This means you must supply a plugin with custom resolveId and load functions. See https://github.com/rollup/rollup/wiki/Plugins for more information` );
if ( typeof process === 'undefined' ) {
error({
code: 'MISSING_PROCESS',
message: `It looks like you're using Rollup in a non-Node.js environment. This means you must supply a plugin with custom resolveId and load functions`,
url: 'https://github.com/rollup/rollup/wiki/Plugins'
});
}
// absolute paths are left untouched
if ( isAbsolute( importee ) ) return addJsExtensionIfNecessary( resolve( importee ) );

11
src/utils/getCodeFrame.js

@ -13,12 +13,15 @@ export default function getCodeFrame ( source, line, column ) {
let lines = source.split( '\n' );
const frameStart = Math.max( 0, line - 3 );
const frameEnd = Math.min( line + 2, lines.length );
const digits = String( frameEnd + 1 ).length;
let frameEnd = Math.min( line + 2, lines.length );
lines = lines.slice( frameStart, frameEnd );
while ( !/\S/.test( lines[ lines.length - 1 ] ) ) lines.pop();
while ( !/\S/.test( lines[ lines.length - 1 ] ) ) {
lines.pop();
frameEnd -= 1;
}
const digits = String( frameEnd ).length;
return lines
.map( ( str, i ) => {

11
src/utils/getExportMode.js

@ -1,7 +1,11 @@
import { keys } from './object.js';
import error from './error.js';
function badExports ( option, keys ) {
throw new Error( `'${option}' was specified for options.exports, but entry module has following exports: ${keys.join(', ')}` );
error({
code: 'INVALID_EXPORT_OPTION',
message: `'${option}' was specified for options.exports, but entry module has following exports: ${keys.join(', ')}`
});
}
export default function getExportMode ( bundle, {exports: exportMode, moduleName, format} ) {
@ -35,7 +39,10 @@ export default function getExportMode ( bundle, {exports: exportMode, moduleName
}
if ( !/(?:default|named|none)/.test( exportMode ) ) {
throw new Error( `options.exports must be 'default', 'named', 'none', 'auto', or left unspecified (defaults to 'auto')` );
error({
code: 'INVALID_EXPORT_OPTION',
message: `options.exports must be 'default', 'named', 'none', 'auto', or left unspecified (defaults to 'auto')`
});
}
return exportMode;

25
src/utils/transform.js

@ -1,4 +1,6 @@
import { decode } from 'sourcemap-codec';
import error from './error.js';
import relativeId from './relativeId.js';
export default function transform ( source, id, plugins ) {
const sourceMapChain = [];
@ -11,6 +13,7 @@ export default function transform ( source, id, plugins ) {
const originalCode = source.code;
let ast = source.ast;
let errored = false;
return plugins.reduce( ( promise, plugin ) => {
return promise.then( previous => {
@ -41,15 +44,21 @@ export default function transform ( source, id, plugins ) {
return result.code;
});
}).catch( err => {
if ( !err.rollupTransform ) {
err.rollupTransform = true;
err.id = id;
err.plugin = plugin.name;
err.message = `Error transforming ${id}${plugin.name ? ` with '${plugin.name}' plugin` : ''}: ${err.message}`;
}
// TODO this all seems a bit hacky
if ( errored ) throw err;
errored = true;
err.plugin = plugin.name;
throw err;
});
}, Promise.resolve( source.code ) )
.then( code => ({ code, originalCode, originalSourceMap, ast, sourceMapChain }) );
.catch( err => {
error({
code: 'BAD_TRANSFORMER',
message: `Error transforming ${relativeId( id )}${err.plugin ? ` with '${err.plugin}' plugin` : ''}: ${err.message}`,
plugin: err.plugin,
id
});
})
.then( code => ({ code, originalCode, originalSourceMap, ast, sourceMapChain }) );
}

9
src/utils/transformBundle.js

@ -1,4 +1,5 @@
import { decode } from 'sourcemap-codec';
import error from './error.js';
export default function transformBundle ( code, plugins, sourceMapChain, options ) {
return plugins.reduce( ( code, plugin ) => {
@ -9,9 +10,11 @@ export default function transformBundle ( code, plugins, sourceMapChain, options
try {
result = plugin.transformBundle( code, { format : options.format } );
} catch ( err ) {
err.plugin = plugin.name;
err.message = `Error transforming bundle${plugin.name ? ` with '${plugin.name}' plugin` : ''}: ${err.message}`;
throw err;
error({
code: 'BAD_BUNDLE_TRANSFORMER',
message: `Error transforming bundle${plugin.name ? ` with '${plugin.name}' plugin` : ''}: ${err.message}`,
plugin: plugin.name
});
}
if ( result == null ) return code;

19
test/function/cannot-call-external-namespace/_config.js

@ -3,10 +3,19 @@ var assert = require( 'assert' );
module.exports = {
description: 'errors if code calls an external namespace',
error: function ( err ) {
assert.equal( err.message, 'Cannot call a namespace (\'foo\')' );
assert.equal( err.file.replace( /\//g, path.sep ), path.resolve( __dirname, 'main.js' ) );
assert.equal( err.pos, 28 );
assert.deepEqual( err.loc, { character: 28, line: 2, column: 0 });
error: {
code: 'CANNOT_CALL_NAMESPACE',
message: `Cannot call a namespace ('foo')`,
pos: 28,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 2,
column: 0
},
frame: `
1: import * as foo from 'foo';
2: foo();
^
`
}
};

19
test/function/cannot-call-internal-namespace/_config.js

@ -3,10 +3,19 @@ var assert = require( 'assert' );
module.exports = {
description: 'errors if code calls an internal namespace',
error: function ( err ) {
assert.equal( err.message, 'Cannot call a namespace (\'foo\')' );
assert.equal( err.file.replace( /\//g, path.sep ), path.resolve( __dirname, 'main.js' ) );
assert.equal( err.pos, 33 );
assert.deepEqual( err.loc, { character: 33, line: 2, column: 0 });
error: {
code: 'CANNOT_CALL_NAMESPACE',
message: `Cannot call a namespace ('foo')`,
pos: 33,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 2,
column: 0
},
frame: `
1: import * as foo from './foo.js';
2: foo();
^
`
}
};

16
test/function/cannot-import-self/_config.js

@ -1,8 +1,20 @@
var path = require( 'path' );
var assert = require( 'assert' );
module.exports = {
description: 'prevents a module importing itself',
error: function ( err ) {
assert.ok( /A module cannot import itself/.test( err.message ) );
error: {
code: 'CANNOT_IMPORT_SELF',
message: `A module cannot import itself`,
pos: 0,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 1,
column: 0
},
frame: `
1: import me from './main';
^
`
}
};

5
test/function/check-resolve-for-entry/_config.js

@ -5,7 +5,8 @@ module.exports = {
options: {
entry: '/not/a/path/that/actually/really/exists'
},
error: function ( err ) {
assert.ok( /Could not resolve entry/.test( err.message ) );
error: {
code: 'UNRESOLVED_ENTRY',
message: 'Could not resolve entry (/not/a/path/that/actually/really/exists)'
}
};

10
test/function/custom-path-resolver-plural-b/_config.js

@ -5,21 +5,21 @@ module.exports = {
options: {
plugins: [
{
resolveId: function () {
resolveId () {
throw new Error( 'nope' );
},
load: function ( id ) {
load ( id ) {
if ( id === 'main' ) return 'assert.ok( false );';
}
},
{
resolveId: function ( importee, importer ) {
resolveId ( importee, importer ) {
return 'main';
}
}
]
},
error: function ( err ) {
assert.equal( err.message, 'nope' );
error: {
message: 'nope'
}
};

19
test/function/default-not-reexported/_config.js

@ -1,8 +1,23 @@
const path = require( 'path' );
const assert = require( 'assert' );
module.exports = {
description: 'default export is not re-exported with export *',
error ( error ) {
assert.equal( error.message, `'default' is not exported by foo.js (imported by main.js). For help fixing this error see https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` );
error: {
code: 'MISSING_EXPORT',
message: `'default' is not exported by foo.js`,
pos: 7,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 1,
column: 7
},
frame: `
1: import def from './foo.js';
^
2:
3: console.log( def );
`,
url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module`
}
};

16
test/function/double-default-export/_config.js

@ -3,7 +3,19 @@ const assert = require( 'assert' );
module.exports = {
description: 'throws on double default exports',
error: err => {
assert.equal( err.message, `Duplicate export 'default' (2:7) in ${path.resolve(__dirname, 'foo.js')}` );
error: {
code: 'PARSE_ERROR',
message: `Duplicate export 'default'`,
pos: 25,
loc: {
file: path.resolve( __dirname, 'foo.js' ),
line: 2,
column: 7
},
frame: `
1: export default 1;
2: export default 2;
^
`
}
};

17
test/function/double-named-export/_config.js

@ -3,7 +3,20 @@ const assert = require( 'assert' );
module.exports = {
description: 'throws on duplicate named exports',
error: err => {
assert.equal( err.message, `Duplicate export 'foo' (3:9) in ${path.resolve(__dirname, 'foo.js')}` );
error: {
code: 'PARSE_ERROR',
message: `Duplicate export 'foo'`,
pos: 38,
loc: {
file: path.resolve( __dirname, 'foo.js' ),
line: 3,
column: 9
},
frame: `
1: var foo = 1;
2: export { foo };
3: export { foo };
^
`
}
};

17
test/function/double-named-reexport/_config.js

@ -3,7 +3,20 @@ const assert = require( 'assert' );
module.exports = {
description: 'throws on duplicate named exports',
error: err => {
assert.equal( err.message, `Duplicate export 'foo' (3:9) in ${path.resolve(__dirname, 'foo.js')}` );
error: {
code: 'PARSE_ERROR',
message: `Duplicate export 'foo'`,
pos: 38,
loc: {
file: path.resolve( __dirname, 'foo.js' ),
line: 3,
column: 9
},
frame: `
1: var foo = 1;
2: export { foo };
3: export { foo } from './bar.js';
^
`
}
};

20
test/function/duplicate-import-fails/_config.js

@ -3,10 +3,22 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows duplicate imports',
error: function ( err ) {
assert.equal( path.normalize( err.file ), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { character: 36, line: 2, column: 9 });
assert.ok( /Duplicated import/.test( err.message ) );
error: {
code: 'DUPLICATE_IMPORT',
message: `Duplicated import 'a'`,
pos: 36,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 2,
column: 9
},
frame: `
1: import { a } from './foo';
2: import { a } from './foo';
^
3:
4: assert.equal(a, 1);
`
}
};

18
test/function/duplicate-import-specifier-fails/_config.js

@ -3,10 +3,20 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows duplicate import specifiers',
error: function ( err ) {
assert.equal( path.normalize( err.file ), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { character: 12, line: 1, column: 12 });
assert.ok( /Duplicated import/.test( err.message ) );
error: {
code: 'DUPLICATE_IMPORT',
message: `Duplicated import 'a'`,
pos: 12,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 1,
column: 12
},
frame: `
1: import { a, a } from './foo';
^
2: assert.equal(a, 1);
`
}
};

19
test/function/export-not-at-top-level-fails/_config.js

@ -3,9 +3,20 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows non-top-level exports',
error: function ( err ) {
assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 2, column: 2 });
assert.ok( /may only appear at the top level/.test( err.message ) );
error: {
code: 'PARSE_ERROR',
message: `'import' and 'export' may only appear at the top level`,
pos: 19,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 2,
column: 2
},
frame: `
1: function foo() {
2: export { foo };
^
3: }
`
}
};

5
test/function/export-type-mismatch-b/_config.js

@ -5,7 +5,8 @@ module.exports = {
bundleOptions: {
exports: 'blah'
},
generateError: function ( err ) {
assert.ok( /options\.exports must be 'default', 'named', 'none', 'auto', or left unspecified/.test( err.message ) );
generateError: {
code: 'INVALID_EXPORT_OPTION',
message: `options.exports must be 'default', 'named', 'none', 'auto', or left unspecified (defaults to 'auto')`
}
};

5
test/function/export-type-mismatch-c/_config.js

@ -5,7 +5,8 @@ module.exports = {
bundleOptions: {
exports: 'none'
},
generateError: function ( err ) {
assert.ok( /'none' was specified for options\.exports/.test( err.message ) );
generateError: {
code: 'INVALID_EXPORT_OPTION',
message: `'none' was specified for options.exports, but entry module has following exports: default`
}
};

5
test/function/export-type-mismatch/_config.js

@ -5,7 +5,8 @@ module.exports = {
bundleOptions: {
exports: 'default'
},
generateError: function ( err ) {
assert.ok( /'default' was specified for options\.exports/.test( err.message ) );
generateError: {
code: 'INVALID_EXPORT_OPTION',
message: `'default' was specified for options.exports, but entry module has following exports: foo`
}
};

19
test/function/import-not-at-top-level-fails/_config.js

@ -3,9 +3,20 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows non-top-level imports',
error: function ( err ) {
assert.equal( path.normalize(err.file), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { line: 2, column: 2 });
assert.ok( /may only appear at the top level/.test( err.message ) );
error: {
code: 'PARSE_ERROR',
message: `'import' and 'export' may only appear at the top level`,
pos: 19,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 2,
column: 2
},
frame: `
1: function foo() {
2: import foo from './foo.js';
^
3: }
`
}
};

2
test/function/import-not-at-top-level-fails/main.js

@ -1,3 +1,3 @@
function foo() {
import foo from './foo';
import foo from './foo.js';
}

19
test/function/import-of-unexported-fails/_config.js

@ -1,8 +1,23 @@
var path = require( 'path' );
var assert = require( 'assert' );
module.exports = {
description: 'marking an imported, but unexported, identifier should throw',
error: function ( err ) {
assert.equal( err.message, `'default' is not exported by empty.js (imported by main.js). For help fixing this error see https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` );
error: {
code: 'MISSING_EXPORT',
message: `'default' is not exported by empty.js`,
pos: 7,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 1,
column: 7
},
frame: `
1: import a from './empty.js';
^
2:
3: a();
`,
url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module`
}
};

6
test/function/load-returns-string-or-null/_config.js

@ -4,12 +4,14 @@ module.exports = {
description: 'throws error if load returns something wacky',
options: {
plugins: [{
name: 'bad-plugin',
load: function () {
return 42;
}
}]
},
error: function ( err ) {
assert.ok( /load hook should return a string, a \{ code, map \} object, or nothing\/null/.test( err.message ) );
error: {
code: 'BAD_LOADER',
message: `Error loading main.js: plugin load hook should return a string, a { code, map } object, or nothing/null`
}
};

19
test/function/namespace-reassign-import-fails/_config.js

@ -3,10 +3,21 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows reassignments to namespace exports',
error: function ( err ) {
assert.equal( path.normalize( err.file ), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { character: 31, line: 3, column: 0 });
assert.ok( /Illegal reassignment/.test( err.message ) );
error: {
code: 'ILLEGAL_NAMESPACE_REASSIGNMENT',
message: `Illegal reassignment to import 'exp'`,
pos: 31,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 3,
column: 0
},
frame: `
1: import * as exp from './foo';
2:
3: exp.foo = 2;
^
`
}
};

19
test/function/namespace-update-import-fails/_config.js

@ -3,10 +3,21 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows updates to namespace exports',
error: function ( err ) {
assert.equal( path.normalize( err.file ), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { character: 31, line: 3, column: 0 });
assert.ok( /Illegal reassignment/.test( err.message ) );
error: {
code: 'ILLEGAL_NAMESPACE_REASSIGNMENT',
message: `Illegal reassignment to import 'exp'`,
pos: 31,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 3,
column: 0
},
frame: `
1: import * as exp from './foo';
2:
3: exp['foo']++;
^
`
}
};

5
test/function/no-relative-external/_config.js

@ -2,7 +2,8 @@ var assert = require( 'assert' );
module.exports = {
description: 'missing relative imports are an error, not a warning',
error: function ( err ) {
assert.ok( /Could not resolve '\.\/missing\.js' from/.test( err.message ) );
error: {
code: 'UNRESOLVED_IMPORT',
message: `Could not resolve './missing.js' from main.js`
}
};

5
test/function/paths-are-case-sensitive/_config.js

@ -2,7 +2,8 @@ var assert = require( 'assert' );
module.exports = {
description: 'insists on correct casing for imports',
error: function ( err ) {
assert.ok( /Could not resolve/.test( err.message ) );
error: {
code: 'UNRESOLVED_IMPORT',
message: `Could not resolve './foo.js' from main.js`
}
};

19
test/function/reassign-import-fails/_config.js

@ -3,10 +3,21 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows assignments to imported bindings',
error: function ( err ) {
assert.ok( /Illegal reassignment/.test( err.message ) );
assert.equal( path.normalize( err.file ), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { character: 113, line: 8, column: 0 });
error: {
code: 'ILLEGAL_REASSIGNMENT',
message: `Illegal reassignment to import 'x'`,
pos: 113,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 8,
column: 0
},
frame: `
6: });
7:
8: x = 10;
^
`
}
};

20
test/function/reassign-import-not-at-top-level-fails/_config.js

@ -3,10 +3,22 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows assignments to imported bindings not at the top level',
error: function ( err ) {
assert.equal( path.normalize( err.file ), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { character: 95, line: 7, column: 2 });
assert.ok( /Illegal reassignment/.test( err.message ) );
error: {
code: 'ILLEGAL_REASSIGNMENT',
message: `Illegal reassignment to import 'x'`,
pos: 95,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 7,
column: 2
},
frame: `
5: }
6: export function bar () {
7: x = 1;
^
8: }
`
}
};

17
test/function/reexport-missing-error/_config.js

@ -1,8 +1,21 @@
var path = require( 'path' );
var assert = require( 'assert' );
module.exports = {
description: 'reexporting a missing identifier should print an error',
error: function ( error ) {
assert.ok( /^'foo' is not exported/.test( error.message ) );
error: {
code: 'MISSING_EXPORT',
message: `'foo' is not exported by empty.js`,
pos: 9,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 1,
column: 9
},
frame: `
1: export { foo as bar } from './empty.js';
^
`,
url: 'https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module'
}
};

9
test/function/report-transform-error-file/_config.js

@ -1,9 +1,11 @@
var path = require( 'path' );
var assert = require( 'assert' );
module.exports = {
description: 'reports which file caused a transform error',
options: {
plugins: [{
name: 'bad-plugin',
transform: function ( code, id ) {
if ( /foo/.test( id ) ) {
throw new Error( 'nope' );
@ -11,7 +13,10 @@ module.exports = {
}
}]
},
error: function ( err ) {
assert.ok( ~err.message.indexOf( 'foo.js' ) );
error: {
code: 'BAD_TRANSFORMER',
message: `Error transforming foo.js with 'bad-plugin' plugin: nope`,
plugin: 'bad-plugin',
id: path.resolve( __dirname, 'foo.js' )
}
};

8
test/function/reports-syntax-error-locations/_config.js

@ -1,8 +0,0 @@
var assert = require( 'assert' );
module.exports = {
description: 'reports syntax error filename',
error: function ( err ) {
assert.ok( /in .+main\.js/.test( err.message ) );
}
};

1
test/function/reports-syntax-error-locations/main.js

@ -1 +0,0 @@
var 42 = answer;

5
test/function/throws-not-found-module/_config.js

@ -3,7 +3,8 @@ var path = require( 'path' );
module.exports = {
description: 'throws error if module is not found',
error: function ( err ) {
assert.equal( err.message, 'Could not resolve \'./mod\' from ' + path.resolve( __dirname, 'main.js' ) );
error: {
code: 'UNRESOLVED_IMPORT',
message: `Could not resolve './mod' from main.js`
}
};

11
test/function/throws-only-first-transform-bundle/_config.js

@ -7,19 +7,20 @@ module.exports = {
{
name: 'plugin1',
transformBundle: function () {
throw Error('Something happend 1');
throw Error( 'Something happened 1' );
}
},
{
name: 'plugin2',
transformBundle: function () {
throw Error('Something happend 2');
throw Error( 'Something happened 2' );
}
}
]
},
generateError: function ( err ) {
assert.equal( err.plugin, 'plugin1' );
assert.equal( err.message, 'Error transforming bundle with \'plugin1\' plugin: Something happend 1' );
generateError: {
code: 'BAD_BUNDLE_TRANSFORMER',
plugin: 'plugin1',
message: `Error transforming bundle with 'plugin1' plugin: Something happened 1`
}
};

19
test/function/throws-only-first-transform/_config.js

@ -7,23 +7,22 @@ module.exports = {
plugins: [
{
name: 'plugin1',
transform: function () {
throw Error('Something happend 1');
transform () {
throw Error( 'Something happened 1' );
}
},
{
name: 'plugin2',
transform: function () {
throw Error('Something happend 2');
transform () {
throw Error( 'Something happened 2' );
}
}
]
},
error: function ( err ) {
var id = path.resolve( __dirname, 'main.js' );
assert.equal( err.rollupTransform, true );
assert.equal( err.id, id );
assert.equal( err.plugin, 'plugin1' );
assert.equal( err.message, 'Error transforming ' + id + ' with \'plugin1\' plugin: Something happend 1' );
error: {
code: 'BAD_TRANSFORMER',
message: `Error transforming main.js with 'plugin1' plugin: Something happened 1`,
plugin: 'plugin1',
id: path.resolve( __dirname, 'main.js' )
}
};

19
test/function/update-expression-of-import-fails/_config.js

@ -3,10 +3,21 @@ var assert = require( 'assert' );
module.exports = {
description: 'disallows updates to imported bindings',
error: function ( err ) {
assert.equal( path.normalize( err.file ), path.resolve( __dirname, 'main.js' ) );
assert.deepEqual( err.loc, { character: 28, line: 3, column: 0 });
assert.ok( /Illegal reassignment/.test( err.message ) );
error: {
code: 'ILLEGAL_REASSIGNMENT',
message: `Illegal reassignment to import 'a'`,
pos: 28,
loc: {
file: path.resolve( __dirname, 'main.js' ),
line: 3,
column: 0
},
frame: `
1: import { a } from './foo';
2:
3: a++;
^
`
}
};

21
test/test.js

@ -79,6 +79,23 @@ function compareWarnings ( actual, expected ) {
);
}
function compareError ( actual, expected ) {
delete actual.stack;
actual = Object.assign( {}, actual, {
message: actual.message
});
if ( actual.frame ) {
actual.frame = actual.frame.replace( /\s+$/gm, '' );
}
if ( expected.frame ) {
expected.frame = expected.frame.slice( 1 ).replace( /^\t+/gm, '' ).replace( /\s+$/gm, '' ).trim();
}
assert.deepEqual( actual, expected );
}
describe( 'rollup', function () {
this.timeout( 10000 );
@ -259,7 +276,7 @@ describe( 'rollup', function () {
}
} catch ( err ) {
if ( config.generateError ) {
config.generateError( err );
compareError( err, config.generateError );
} else {
unintendedError = err;
}
@ -324,7 +341,7 @@ describe( 'rollup', function () {
if ( unintendedError ) throw unintendedError;
}, err => {
if ( config.error ) {
config.error( err );
compareError( err, config.error );
} else {
throw err;
}

Loading…
Cancel
Save