diff --git a/bin/src/runRollup.js b/bin/src/runRollup.js index 63bbd0c..9ef3b19 100644 --- a/bin/src/runRollup.js +++ b/bin/src/runRollup.js @@ -1,7 +1,9 @@ import { realpathSync } from 'fs'; import * as rollup from 'rollup'; import relative from 'require-relative'; +import * as chalk from 'chalk'; import handleError from './handleError'; +import relativeId from '../../src/utils/relativeId.js'; import SOURCEMAPPING_URL from './sourceMappingUrl.js'; import { install as installSourcemapSupport } from 'source-map-support'; @@ -60,9 +62,8 @@ export default function runRollup ( command ) { rollup.rollup({ entry: config, onwarn: message => { - // TODO use warning codes instead of this hackery - if ( /treating it as an external dependency/.test( message ) ) return; - stderr( message ); + if ( message.code === 'UNRESOLVED_IMPORT' ) return; + stderr( message.toString() ); } }).then( bundle => { const { code } = bundle.generate({ @@ -121,7 +122,7 @@ function execute ( options, command ) { const optionsExternal = options.external; if ( command.globals ) { - let globals = Object.create( null ); + const globals = Object.create( null ); command.globals.split( ',' ).forEach( str => { const names = str.split( ':' ); @@ -144,7 +145,32 @@ function execute ( options, command ) { external = ( optionsExternal || [] ).concat( commandExternal ); } - options.onwarn = options.onwarn || stderr; + if ( !options.onwarn ) { + const seen = new Set(); + + options.onwarn = warning => { + const str = warning.toString(); + + if ( seen.has( str ) ) return; + seen.add( str ); + + stderr( `⚠️ ${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( '' ); + }; + } options.external = external; diff --git a/package.json b/package.json index f7af247..4911fac 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "eslint-plugin-import": "^2.2.0", "is-reference": "^1.0.0", "istanbul": "^0.4.3", + "locate-character": "^2.0.0", "magic-string": "^0.15.2", "minimist": "^1.2.0", "mocha": "^3.0.0", diff --git a/rollup.config.cli.js b/rollup.config.cli.js index 982f6b0..5c0f673 100644 --- a/rollup.config.cli.js +++ b/rollup.config.cli.js @@ -15,7 +15,9 @@ export default { buble(), commonjs({ include: 'node_modules/**', - namedExports: { chalk: [ 'red', 'cyan', 'grey' ] } + namedExports: { + chalk: [ 'yellow', 'red', 'cyan', 'grey', 'dim', 'bold' ] + } }), nodeResolve({ main: true diff --git a/src/Bundle.js b/src/Bundle.js index 1166cb2..79cd8d0 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -188,7 +188,10 @@ export default class Bundle { `'${unused[0]}' is` : `${unused.slice( 0, -1 ).map( name => `'${name}'` ).join( ', ' )} and '${unused.pop()}' are`; - this.onwarn( `${names} imported from external module '${module.id}' but never used` ); + this.warn({ + code: 'UNUSED_EXTERNAL_IMPORT', + message: `${names} imported from external module '${module.id}' but never used` + }); }); this.orderedModules = this.sort(); @@ -309,7 +312,10 @@ export default class Bundle { keys( exportAllModule.exportsAll ).forEach( name => { if ( name in module.exportsAll ) { - this.onwarn( `Conflicting namespaces: ${module.id} re-exports '${name}' from both ${module.exportsAll[ name ]} (will be ignored) and ${exportAllModule.exportsAll[ name ]}.` ); + this.warn({ + code: 'NAMESPACE_CONFLICT', + message: `Conflicting namespaces: ${relativeId( module.id )} re-exports '${name}' from both ${relativeId( module.exportsAll[ name ] )} (will be ignored) and ${relativeId( exportAllModule.exportsAll[ name ] )}` + }); } module.exportsAll[ name ] = exportAllModule.exportsAll[ name ]; }); @@ -333,7 +339,11 @@ export default class Bundle { if ( !resolvedId && !isExternal ) { if ( isRelative( source ) ) throw new Error( `Could not resolve '${source}' from ${module.id}` ); - this.onwarn( `'${source}' is imported by ${relativeId( module.id )}, but could not be resolved – treating it as an external dependency. For help see https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency` ); + this.warn({ + code: 'UNRESOLVED_IMPORT', + message: `'${source}' is imported by ${relativeId( module.id )}, but could not be resolved – treating it as an external dependency`, + url: 'https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency' + }); isExternal = true; } @@ -380,7 +390,11 @@ export default class Bundle { render ( options = {} ) { if ( options.format === 'es6' ) { - this.onwarn( 'The es6 format is deprecated – use `es` instead' ); + this.warn({ + code: 'DEPRECATED_ES6', + message: 'The es6 format is deprecated – use `es` instead' + }); + options.format = 'es'; } @@ -404,7 +418,10 @@ export default class Bundle { }); if ( !magicString.toString().trim() && this.entryModule.getExports().length === 0 ) { - this.onwarn( 'Generated an empty bundle' ); + this.warn({ + code: 'EMPTY_BUNDLE', + message: 'Generated an empty bundle' + }); } timeEnd( 'render modules' ); @@ -470,7 +487,7 @@ export default class Bundle { if ( typeof map.mappings === 'string' ) { map.mappings = decode( map.mappings ); } - map = collapseSourcemaps( file, map, usedModules, bundleSourcemapChain, this.onwarn ); + map = collapseSourcemaps( this, file, map, usedModules, bundleSourcemapChain ); } else { map = magicString.generateMap({ file, includeContent: true }); } @@ -535,6 +552,7 @@ export default class Bundle { for ( i += 1; i < ordered.length; i += 1 ) { const b = ordered[i]; + // TODO reinstate this! it no longer works if ( stronglyDependsOn[ a.id ][ b.id ] ) { // somewhere, there is a module that imports b before a. Because // b imports a, a is placed before b. We need to find the module @@ -566,4 +584,16 @@ export default class Bundle { return ordered; } + + warn ( warning ) { + warning.toString = () => { + if ( warning.loc ) { + return `${relativeId( warning.loc.file )} (${warning.loc.line}:${warning.loc.column}) ${warning.message}`; + } + + return warning.message; + }; + + this.onwarn( warning ); + } } diff --git a/src/Module.js b/src/Module.js index 71db738..81926c3 100644 --- a/src/Module.js +++ b/src/Module.js @@ -1,10 +1,11 @@ -import { timeStart, timeEnd } from './utils/flushTime.js'; import { parse } from 'acorn/src/index.js'; import MagicString from 'magic-string'; +import { locate } from 'locate-character'; +import { timeStart, timeEnd } from './utils/flushTime.js'; import { assign, blank, deepClone, keys } from './utils/object.js'; import { basename, extname } from './utils/path.js'; -import getLocation from './utils/getLocation.js'; import makeLegalIdentifier from './utils/makeLegalIdentifier.js'; +import getCodeFrame from './utils/getCodeFrame.js'; import { SOURCEMAPPING_URL_RE } from './utils/sourceMappingURL.js'; import error from './utils/error.js'; import relativeId from './utils/relativeId.js'; @@ -179,7 +180,12 @@ export default class Module { this.exports[ exportedName ] = { localName }; }); } else { - this.bundle.onwarn( `Module ${this.id} has an empty export declaration` ); + // TODO is this really necessary? `export {}` is valid JS, and + // might be used as a hint that this is indeed a module + this.warn({ + code: 'EMPTY_EXPORT', + message: `Empty export declaration` + }, node.start ); } } } @@ -195,7 +201,7 @@ export default class Module { if ( this.imports[ localName ] ) { const err = new Error( `Duplicated import '${localName}'` ); err.file = this.id; - err.loc = getLocation( this.code, specifier.start ); + err.loc = locate( this.code, specifier.start, { offsetLine: 1 }); throw err; } @@ -361,7 +367,7 @@ export default class Module { 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: getLocation( this.code, importDeclaration.specifier.start ) + loc: locate( this.code, importDeclaration.specifier.start, { offsetLine: 1 }) }); } @@ -381,7 +387,7 @@ export default class Module { error({ message: `'${reexportDeclaration.localName}' is not exported by '${reexportDeclaration.module.id}' (imported by '${this.id}')`, file: this.id, - loc: getLocation( this.code, reexportDeclaration.start ) + loc: locate( this.code, reexportDeclaration.start, { offsetLine: 1 }) }); } @@ -405,4 +411,17 @@ export default class Module { if ( declaration ) return declaration; } } + + warn ( warning, pos ) { + if ( pos !== undefined ) { + warning.pos = pos; + + const { line, column } = locate( this.code, pos, { offsetLine: 1 }); // TODO trace sourcemaps + + warning.loc = { file: this.id, line, column }; + warning.frame = getCodeFrame( this.code, line, column ); + } + + this.bundle.warn( warning ); + } } diff --git a/src/ast/Node.js b/src/ast/Node.js index 6a80ede..da36211 100644 --- a/src/ast/Node.js +++ b/src/ast/Node.js @@ -1,5 +1,5 @@ +import { locate } from 'locate-character'; import { UNKNOWN } from './values.js'; -import getLocation from '../utils/getLocation.js'; export default class Node { bind ( scope ) { @@ -74,7 +74,7 @@ export default class Node { locate () { // useful for debugging - const location = getLocation( this.module.code, this.start ); + const location = locate( this.module.code, this.start, { offsetLine: 1 }); location.file = this.module.id; location.toString = () => JSON.stringify( location ); diff --git a/src/ast/nodes/CallExpression.js b/src/ast/nodes/CallExpression.js index d275100..4880cb5 100644 --- a/src/ast/nodes/CallExpression.js +++ b/src/ast/nodes/CallExpression.js @@ -1,4 +1,4 @@ -import getLocation from '../../utils/getLocation.js'; +import { locate } from 'locate-character'; import error from '../../utils/error.js'; import Node from '../Node.js'; import isProgramLevel from '../utils/isProgramLevel.js'; @@ -14,12 +14,16 @@ export default class CallExpression extends Node { message: `Cannot call a namespace ('${this.callee.name}')`, file: this.module.id, pos: this.start, - loc: getLocation( this.module.code, this.start ) + loc: locate( this.module.code, this.start, { offsetLine: 1 }) }); } if ( this.callee.name === 'eval' && declaration.isGlobal ) { - this.module.bundle.onwarn( `Use of \`eval\` (in ${this.module.id}) is strongly discouraged, as it poses security risks and may cause issues with minification. See https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval for more details` ); + this.module.warn({ + code: 'EVAL', + message: `Use of eval is strongly discouraged, as it poses security risks and may cause issues with minification`, + url: 'https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval' + }, this.start ); } } diff --git a/src/ast/nodes/ExportDefaultDeclaration.js b/src/ast/nodes/ExportDefaultDeclaration.js index d70bd10..7027291 100644 --- a/src/ast/nodes/ExportDefaultDeclaration.js +++ b/src/ast/nodes/ExportDefaultDeclaration.js @@ -1,6 +1,4 @@ import Node from '../Node.js'; -import getLocation from '../../utils/getLocation.js'; -import relativeId from '../../utils/relativeId.js'; const functionOrClassDeclaration = /^(?:Function|Class)Declaration/; @@ -74,8 +72,11 @@ export default class ExportDefaultDeclaration extends Node { const newlineSeparated = /\n/.test( code.original.slice( start, end ) ); if ( newlineSeparated ) { - const { line, column } = getLocation( this.module.code, this.declaration.start ); - this.module.bundle.onwarn( `${relativeId( this.module.id )} (${line}:${column}) Ambiguous default export (is a call expression, but looks like a function declaration). See https://github.com/rollup/rollup/wiki/Troubleshooting#ambiguous-default-export` ); + this.module.warn({ + code: 'AMBIGUOUS_DEFAULT_EXPORT', + message: `Ambiguous default export (is a call expression, but looks like a function declaration)`, + url: 'https://github.com/rollup/rollup/wiki/Troubleshooting#ambiguous-default-export' + }, this.declaration.start ); } } } diff --git a/src/ast/nodes/MemberExpression.js b/src/ast/nodes/MemberExpression.js index bcdcf26..d745d7d 100644 --- a/src/ast/nodes/MemberExpression.js +++ b/src/ast/nodes/MemberExpression.js @@ -1,5 +1,4 @@ import isReference from 'is-reference'; -import getLocation from '../../utils/getLocation.js'; import relativeId from '../../utils/relativeId.js'; import Node from '../Node.js'; import { UNKNOWN } from '../values.js'; @@ -34,8 +33,11 @@ export default class MemberExpression extends Node { declaration = declaration.module.traceExport( part.name ); if ( !declaration ) { - const { line, column } = getLocation( this.module.code, this.start ); - this.module.bundle.onwarn( `${relativeId( this.module.id )} (${line}:${column}) '${part.name}' is not exported by '${relativeId( exporterId )}'. See https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` ); + this.module.warn({ + code: 'MISSING_EXPORT', + message: `'${part.name}' is not exported by '${relativeId( exporterId )}'`, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` + }, part.start ); this.replacement = 'undefined'; return; } diff --git a/src/ast/nodes/ThisExpression.js b/src/ast/nodes/ThisExpression.js index ae35173..2429c65 100644 --- a/src/ast/nodes/ThisExpression.js +++ b/src/ast/nodes/ThisExpression.js @@ -1,8 +1,4 @@ import Node from '../Node.js'; -import getLocation from '../../utils/getLocation.js'; -import relativeId from '../../utils/relativeId.js'; - -const warning = `The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten. See https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined for more information`; export default class ThisExpression extends Node { initialise ( scope ) { @@ -11,9 +7,11 @@ export default class ThisExpression extends Node { if ( lexicalBoundary.isModuleScope ) { this.alias = this.module.context; if ( this.alias === 'undefined' ) { - const { line, column } = getLocation( this.module.code, this.start ); - const detail = `${relativeId( this.module.id )} (${line}:${column + 1})`; // use one-based column number convention - this.module.bundle.onwarn( `${detail} ${warning}` ); + this.module.warn({ + code: 'THIS_IS_UNDEFINED', + message: `The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten`, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined` + }, this.start ); } } } diff --git a/src/ast/nodes/shared/disallowIllegalReassignment.js b/src/ast/nodes/shared/disallowIllegalReassignment.js index a40969f..5d338af 100644 --- a/src/ast/nodes/shared/disallowIllegalReassignment.js +++ b/src/ast/nodes/shared/disallowIllegalReassignment.js @@ -1,4 +1,4 @@ -import getLocation from '../../../utils/getLocation.js'; +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) @@ -10,7 +10,7 @@ export default function disallowIllegalReassignment ( scope, node ) { message: `Illegal reassignment to import '${node.object.name}'`, file: node.module.id, pos: node.start, - loc: getLocation( node.module.code, node.start ) + loc: locate( node.module.code, node.start, { offsetLine: 1 }) }); } } @@ -21,7 +21,7 @@ export default function disallowIllegalReassignment ( scope, node ) { message: `Illegal reassignment to import '${node.name}'`, file: node.module.id, pos: node.start, - loc: getLocation( node.module.code, node.start ) + loc: locate( node.module.code, node.start, { offsetLine: 1 }) }); } } diff --git a/src/ast/scopes/ModuleScope.js b/src/ast/scopes/ModuleScope.js index 4c8804c..f0f5f7a 100644 --- a/src/ast/scopes/ModuleScope.js +++ b/src/ast/scopes/ModuleScope.js @@ -1,4 +1,5 @@ import { forOwn } from '../../utils/object.js'; +import relativeId from '../../utils/relativeId.js'; import Scope from './Scope.js'; export default class ModuleScope extends Scope { @@ -26,7 +27,10 @@ export default class ModuleScope extends Scope { if ( specifier.name !== '*' ) { const declaration = specifier.module.traceExport( specifier.name ); if ( !declaration ) { - this.module.bundle.onwarn( `Non-existent export '${specifier.name}' is imported from ${specifier.module.id} by ${this.module.id}` ); + this.module.warn({ + code: 'NON_EXISTENT_EXPORT', + message: `Non-existent export '${specifier.name}' is imported from ${relativeId( specifier.module.id )}` + }, specifier.specifier.start ); return; } diff --git a/src/finalisers/iife.js b/src/finalisers/iife.js index f6d702e..2d6121e 100644 --- a/src/finalisers/iife.js +++ b/src/finalisers/iife.js @@ -25,7 +25,7 @@ function setupNamespace ( keypath ) { } export default function iife ( bundle, magicString, { exportMode, indentString, intro, outro }, options ) { - const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle.onwarn ); + const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle ); const name = options.moduleName; const isNamespaced = name && ~name.indexOf( '.' ); diff --git a/src/finalisers/shared/getGlobalNameMaker.js b/src/finalisers/shared/getGlobalNameMaker.js index 676b416..7a1d622 100644 --- a/src/finalisers/shared/getGlobalNameMaker.js +++ b/src/finalisers/shared/getGlobalNameMaker.js @@ -1,11 +1,15 @@ -export default function getGlobalNameMaker ( globals, onwarn ) { +export default function getGlobalNameMaker ( globals, bundle ) { const fn = typeof globals === 'function' ? globals : id => globals[ id ]; return function ( module ) { const name = fn( module.id ); if ( name ) return name; - onwarn( `No name was provided for external module '${module.id}' in options.globals – guessing '${module.name}'` ); + bundle.warn({ + code: 'MISSING_GLOBAL_NAME', + message: `No name was provided for external module '${module.id}' in options.globals – guessing '${module.name}'` + }); + return module.name; }; } diff --git a/src/finalisers/shared/warnOnBuiltins.js b/src/finalisers/shared/warnOnBuiltins.js index e5ac1ba..c4d63f6 100644 --- a/src/finalisers/shared/warnOnBuiltins.js +++ b/src/finalisers/shared/warnOnBuiltins.js @@ -35,5 +35,8 @@ export default function warnOnBuiltins ( bundle ) { `module ('${externalBuiltins[0]}')` : `modules (${externalBuiltins.slice( 0, -1 ).map( name => `'${name}'` ).join( ', ' )} and '${externalBuiltins.pop()}')`; - bundle.onwarn( `Creating a browser bundle that depends on Node.js built-in ${detail}. You might need to include https://www.npmjs.com/package/rollup-plugin-node-builtins` ); + bundle.warn({ + code: 'MISSING_NODE_BUILTINS', + message: `Creating a browser bundle that depends on Node.js built-in ${detail}. You might need to include https://www.npmjs.com/package/rollup-plugin-node-builtins` + }); } diff --git a/src/finalisers/umd.js b/src/finalisers/umd.js index c176242..655c77a 100644 --- a/src/finalisers/umd.js +++ b/src/finalisers/umd.js @@ -33,7 +33,7 @@ export default function umd ( bundle, magicString, { exportMode, indentString, i warnOnBuiltins( bundle ); - const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle.onwarn ); + const globalNameMaker = getGlobalNameMaker( options.globals || blank(), bundle ); const amdDeps = bundle.externalModules.map( quotePath ); const cjsDeps = bundle.externalModules.map( req ); diff --git a/src/utils/collapseSourcemaps.js b/src/utils/collapseSourcemaps.js index 7a504ba..122a148 100644 --- a/src/utils/collapseSourcemaps.js +++ b/src/utils/collapseSourcemaps.js @@ -98,7 +98,7 @@ class Link { } } -export default function collapseSourcemaps ( file, map, modules, bundleSourcemapChain, onwarn ) { +export default function collapseSourcemaps ( bundle, file, map, modules, bundleSourcemapChain ) { const moduleSources = modules.filter( module => !module.excludeFromSourcemap ).map( module => { let sourceMapChain = module.sourceMapChain; @@ -127,7 +127,11 @@ export default function collapseSourcemaps ( file, map, modules, bundleSourcemap sourceMapChain.forEach( map => { if ( map.missing ) { - onwarn( `Sourcemap is likely to be incorrect: a plugin${map.plugin ? ` ('${map.plugin}')` : ``} was used to transform files, but didn't generate a sourcemap for the transformation. Consult https://github.com/rollup/rollup/wiki/Troubleshooting and the plugin documentation for more information` ); + bundle.warn({ + code: 'SOURCEMAP_BROKEN', + message: `Sourcemap is likely to be incorrect: a plugin${map.plugin ? ` ('${map.plugin}')` : ``} was used to transform files, but didn't generate a sourcemap for the transformation. Consult the plugin documentation for help`, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#sourcemap-is-likely-to-be-incorrect` + }); map = { names: [], diff --git a/src/utils/defaults.js b/src/utils/defaults.js index 8db3c0b..c5dd054 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -45,9 +45,10 @@ export function resolveId ( importee, importer ) { export function makeOnwarn () { const warned = blank(); - return msg => { - if ( msg in warned ) return; - console.error( msg ); //eslint-disable-line no-console - warned[ msg ] = true; + return warning => { + const str = warning.toString(); + if ( str in warned ) return; + console.error( str ); //eslint-disable-line no-console + warned[ str ] = true; }; } diff --git a/src/utils/getCodeFrame.js b/src/utils/getCodeFrame.js new file mode 100644 index 0000000..369829b --- /dev/null +++ b/src/utils/getCodeFrame.js @@ -0,0 +1,38 @@ +function spaces ( i ) { + let result = ''; + while ( i-- ) result += ' '; + return result; +} + + +function tabsToSpaces ( str ) { + return str.replace( /^\t+/, match => match.split( '\t' ).join( ' ' ) ); +} + +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; + + lines = lines.slice( frameStart, frameEnd ); + while ( !/\S/.test( lines[ lines.length - 1 ] ) ) lines.pop(); + + return lines + .map( ( str, i ) => { + const isErrorLine = frameStart + i + 1 === line; + + let lineNum = String( i + frameStart + 1 ); + while ( lineNum.length < digits ) lineNum = ` ${lineNum}`; + + if ( isErrorLine ) { + const indicator = spaces( digits + 2 + tabsToSpaces( str.slice( 0, column ) ).length ) + '^'; + return `${lineNum}: ${tabsToSpaces( str )}\n${indicator}`; + } + + return `${lineNum}: ${tabsToSpaces( str )}`; + }) + .join( '\n' ); +} diff --git a/src/utils/getExportMode.js b/src/utils/getExportMode.js index a3d00a0..d525550 100644 --- a/src/utils/getExportMode.js +++ b/src/utils/getExportMode.js @@ -24,7 +24,11 @@ export default function getExportMode ( bundle, {exports: exportMode, moduleName exportMode = 'default'; } else { if ( bundle.entryModule.exports.default && format !== 'es') { - bundle.onwarn( `Using named and default exports together. Consumers of your bundle will have to use ${moduleName || 'bundle'}['default'] to access the default export, which may not be what you want. Use \`exports: 'named'\` to disable this warning. See https://github.com/rollup/rollup/wiki/JavaScript-API#exports for more information` ); + bundle.warn({ + code: 'MIXED_EXPORTS', + message: `Using named and default exports together. Consumers of your bundle will have to use ${moduleName || 'bundle'}['default'] to access the default export, which may not be what you want. Use \`exports: 'named'\` to disable this warning`, + url: `https://github.com/rollup/rollup/wiki/JavaScript-API#exports` + }); } exportMode = 'named'; } diff --git a/src/utils/getLocation.js b/src/utils/getLocation.js deleted file mode 100644 index 41ca492..0000000 --- a/src/utils/getLocation.js +++ /dev/null @@ -1,20 +0,0 @@ -export default function getLocation ( source, charIndex ) { - const lines = source.split( '\n' ); - const len = lines.length; - - let lineStart = 0; - let i; - - for ( i = 0; i < len; i += 1 ) { - const line = lines[i]; - const lineEnd = lineStart + line.length + 1; // +1 for newline - - if ( lineEnd > charIndex ) { - return { line: i + 1, column: charIndex - lineStart }; - } - - lineStart = lineEnd; - } - - throw new Error( 'Could not determine location of character' ); -} diff --git a/test/function/assign-namespace-to-var/_config.js b/test/function/assign-namespace-to-var/_config.js index 2eb7e9c..a8c94ea 100644 --- a/test/function/assign-namespace-to-var/_config.js +++ b/test/function/assign-namespace-to-var/_config.js @@ -2,9 +2,10 @@ const assert = require( 'assert' ); module.exports = { description: 'allows a namespace to be assigned to a variable', - warnings: warnings => { - assert.deepEqual( warnings, [ - 'Generated an empty bundle' - ]); - } + warnings: [ + { + code: 'EMPTY_BUNDLE', + message: 'Generated an empty bundle' + } + ] }; diff --git a/test/function/cannot-call-external-namespace/_config.js b/test/function/cannot-call-external-namespace/_config.js index 263c14a..5ed02e2 100644 --- a/test/function/cannot-call-external-namespace/_config.js +++ b/test/function/cannot-call-external-namespace/_config.js @@ -7,6 +7,6 @@ module.exports = { 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, { line: 2, column: 0 }); + assert.deepEqual( err.loc, { character: 28, line: 2, column: 0 }); } }; diff --git a/test/function/cannot-call-internal-namespace/_config.js b/test/function/cannot-call-internal-namespace/_config.js index ceb42c3..d347008 100644 --- a/test/function/cannot-call-internal-namespace/_config.js +++ b/test/function/cannot-call-internal-namespace/_config.js @@ -7,6 +7,6 @@ module.exports = { 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, { line: 2, column: 0 }); + assert.deepEqual( err.loc, { character: 33, line: 2, column: 0 }); } }; diff --git a/test/function/custom-path-resolver-async/_config.js b/test/function/custom-path-resolver-async/_config.js index 83a10c1..3924db7 100644 --- a/test/function/custom-path-resolver-async/_config.js +++ b/test/function/custom-path-resolver-async/_config.js @@ -21,11 +21,13 @@ module.exports = { } }] }, - warnings: function ( warnings ) { - assert.deepEqual( warnings, [ - `'path' is imported by main.js, but could not be resolved – treating it as an external dependency. For help see https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency` - ]); - }, + warnings: [ + { + code: 'UNRESOLVED_IMPORT', + message: `'path' is imported by main.js, but could not be resolved – treating it as an external dependency`, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency` + } + ], exports: function ( exports ) { assert.strictEqual( exports.path, require( 'path' ) ); } diff --git a/test/function/custom-path-resolver-sync/_config.js b/test/function/custom-path-resolver-sync/_config.js index 074f671..f32cb9a 100644 --- a/test/function/custom-path-resolver-sync/_config.js +++ b/test/function/custom-path-resolver-sync/_config.js @@ -13,11 +13,13 @@ module.exports = { } }] }, - warnings: function ( warnings ) { - assert.deepEqual( warnings, [ - `'path' is imported by main.js, but could not be resolved – treating it as an external dependency. For help see https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency` - ]); - }, + warnings: [ + { + code: 'UNRESOLVED_IMPORT', + message: `'path' is imported by main.js, but could not be resolved – treating it as an external dependency`, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency` + } + ], exports: function ( exports ) { assert.strictEqual( exports.path, require( 'path' ) ); } diff --git a/test/function/does-not-hang-on-missing-module/_config.js b/test/function/does-not-hang-on-missing-module/_config.js index 901bfae..aeb5386 100644 --- a/test/function/does-not-hang-on-missing-module/_config.js +++ b/test/function/does-not-hang-on-missing-module/_config.js @@ -2,11 +2,13 @@ var assert = require( 'assert' ); module.exports = { description: 'does not hang on missing module (#53)', - warnings: warnings => { - assert.deepEqual( warnings, [ - `'unlessYouCreatedThisFileForSomeReason' is imported by main.js, but could not be resolved – treating it as an external dependency. For help see https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency` - ]); - }, + warnings: [ + { + code: 'UNRESOLVED_IMPORT', + message: `'unlessYouCreatedThisFileForSomeReason' is imported by main.js, but could not be resolved – treating it as an external dependency`, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency` + } + ], runtimeError: function ( error ) { assert.equal( "Cannot find module 'unlessYouCreatedThisFileForSomeReason'", error.message ); } diff --git a/test/function/double-named-export-from/_config.js b/test/function/double-named-export-from/_config.js deleted file mode 100644 index b3885d2..0000000 --- a/test/function/double-named-export-from/_config.js +++ /dev/null @@ -1,13 +0,0 @@ -const { resolve } = require('path'); -const assert = require( 'assert' ); - -const r = path => resolve( __dirname, path ); - -module.exports = { - description: 'throws on duplicate export * from', - warnings ( warnings ) { - assert.deepEqual( warnings, [ - `Conflicting namespaces: ${r('main.js')} re-exports 'foo' from both ${r('foo.js')} (will be ignored) and ${r('deep.js')}.` - ]); - } -}; diff --git a/test/function/duplicate-import-fails/_config.js b/test/function/duplicate-import-fails/_config.js index 4083690..492ac2f 100644 --- a/test/function/duplicate-import-fails/_config.js +++ b/test/function/duplicate-import-fails/_config.js @@ -4,8 +4,8 @@ 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, { line: 2, column: 9 }); + 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 ) ); } }; diff --git a/test/function/duplicate-import-specifier-fails/_config.js b/test/function/duplicate-import-specifier-fails/_config.js index e3957b5..58446af 100644 --- a/test/function/duplicate-import-specifier-fails/_config.js +++ b/test/function/duplicate-import-specifier-fails/_config.js @@ -4,8 +4,8 @@ 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, { line: 1, column: 12 }); + 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 ) ); } }; diff --git a/test/function/empty-exports/_config.js b/test/function/empty-exports/_config.js index 8030197..e928a59 100644 --- a/test/function/empty-exports/_config.js +++ b/test/function/empty-exports/_config.js @@ -1,12 +1,23 @@ -const assert = require( 'assert' ); -const path = require( 'path' ); - module.exports = { description: 'warns on export {}, but does not fail', - warnings: warnings => { - assert.deepEqual( warnings, [ - `Module ${path.resolve( __dirname, 'main.js' )} has an empty export declaration`, - 'Generated an empty bundle' - ]); - } + warnings: [ + { + code: 'EMPTY_EXPORT', + message: 'Empty export declaration', + pos: 0, + loc: { + file: require( 'path' ).resolve( __dirname, 'main.js' ), + line: 1, + column: 0 + }, + frame: ` + 1: export {}; + ^ + ` + }, + { + code: 'EMPTY_BUNDLE', + message: 'Generated an empty bundle' + } + ] }; diff --git a/test/function/module-tree/_config.js b/test/function/module-tree/_config.js index 9662844..601ee7d 100644 --- a/test/function/module-tree/_config.js +++ b/test/function/module-tree/_config.js @@ -34,9 +34,10 @@ module.exports = { } ]); }, - warnings: warnings => { - assert.deepEqual( warnings, [ - 'Generated an empty bundle' - ]); - } + warnings: [ + { + code: 'EMPTY_BUNDLE', + message: 'Generated an empty bundle' + } + ] }; diff --git a/test/function/namespace-missing-export/_config.js b/test/function/namespace-missing-export/_config.js index 6ecf3b9..485a1ba 100644 --- a/test/function/namespace-missing-export/_config.js +++ b/test/function/namespace-missing-export/_config.js @@ -1,9 +1,21 @@ -var assert = require( 'assert' ); - module.exports = { - options: { - onwarn: function ( msg ) { - assert.equal( msg, `main.js (3:21) 'foo' is not exported by 'empty.js'. See https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` ); + warnings: [ + { + code: 'MISSING_EXPORT', + message: `'foo' is not exported by 'empty.js'`, + pos: 61, + loc: { + file: require( 'path' ).resolve( __dirname, 'main.js' ), + line: 3, + column: 25 + }, + frame: ` + 1: import * as mod from './empty.js'; + 2: + 3: assert.equal( typeof mod.foo, 'undefined' ); + ^ + `, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module` } - } + ] }; diff --git a/test/function/namespace-reassign-import-fails/_config.js b/test/function/namespace-reassign-import-fails/_config.js index 2d606f4..a76a36a 100644 --- a/test/function/namespace-reassign-import-fails/_config.js +++ b/test/function/namespace-reassign-import-fails/_config.js @@ -4,8 +4,8 @@ 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, { line: 3, column: 0 }); + 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 ) ); } }; diff --git a/test/function/namespace-update-import-fails/_config.js b/test/function/namespace-update-import-fails/_config.js index 4cff5c0..dc8e0f3 100644 --- a/test/function/namespace-update-import-fails/_config.js +++ b/test/function/namespace-update-import-fails/_config.js @@ -4,8 +4,8 @@ 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, { line: 3, column: 0 }); + 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 ) ); } }; diff --git a/test/function/reassign-import-fails/_config.js b/test/function/reassign-import-fails/_config.js index 22591fe..4854d02 100644 --- a/test/function/reassign-import-fails/_config.js +++ b/test/function/reassign-import-fails/_config.js @@ -5,8 +5,8 @@ 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, { line: 8, column: 0 }); + assert.equal( path.normalize( err.file ), path.resolve( __dirname, 'main.js' ) ); + assert.deepEqual( err.loc, { character: 113, line: 8, column: 0 }); } }; diff --git a/test/function/reassign-import-not-at-top-level-fails/_config.js b/test/function/reassign-import-not-at-top-level-fails/_config.js index 6e00786..b4cba92 100644 --- a/test/function/reassign-import-not-at-top-level-fails/_config.js +++ b/test/function/reassign-import-not-at-top-level-fails/_config.js @@ -4,8 +4,8 @@ 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, { line: 7, column: 2 }); + 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 ) ); } }; diff --git a/test/function/unused-import/_config.js b/test/function/unused-import/_config.js index b9e9b85..0f99f59 100644 --- a/test/function/unused-import/_config.js +++ b/test/function/unused-import/_config.js @@ -2,11 +2,19 @@ const assert = require( 'assert' ); module.exports = { description: 'warns on unused imports ([#595])', - warnings: warnings => { - assert.deepEqual( warnings, [ - `'external' is imported by main.js, but could not be resolved – treating it as an external dependency. For help see https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency`, - `'unused', 'notused' and 'neverused' are imported from external module 'external' but never used`, - `Generated an empty bundle` - ]); - } + warnings: [ + { + code: 'UNRESOLVED_IMPORT', + message: `'external' is imported by main.js, but could not be resolved – treating it as an external dependency`, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency` + }, + { + code: 'UNUSED_EXTERNAL_IMPORT', + message: `'unused', 'notused' and 'neverused' are imported from external module 'external' but never used` + }, + { + code: 'EMPTY_BUNDLE', + message: `Generated an empty bundle` + } + ] }; diff --git a/test/function/update-expression-of-import-fails/_config.js b/test/function/update-expression-of-import-fails/_config.js index 5f5c27c..cf72241 100644 --- a/test/function/update-expression-of-import-fails/_config.js +++ b/test/function/update-expression-of-import-fails/_config.js @@ -4,8 +4,8 @@ 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, { line: 3, column: 0 }); + 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 ) ); } }; diff --git a/test/function/warn-on-ambiguous-function-export/_config.js b/test/function/warn-on-ambiguous-function-export/_config.js index 1b83422..baa08bc 100644 --- a/test/function/warn-on-ambiguous-function-export/_config.js +++ b/test/function/warn-on-ambiguous-function-export/_config.js @@ -2,9 +2,23 @@ const assert = require( 'assert' ); module.exports = { description: 'uses original name of default export function (#1011)', - warnings: warnings => { - assert.deepEqual( warnings, [ - 'foo.js (1:15) Ambiguous default export (is a call expression, but looks like a function declaration). See https://github.com/rollup/rollup/wiki/Troubleshooting#ambiguous-default-export' - ]); - } + warnings: [ + { + code: 'AMBIGUOUS_DEFAULT_EXPORT', + message: `Ambiguous default export (is a call expression, but looks like a function declaration)`, + pos: 15, + loc: { + file: require( 'path' ).resolve( __dirname, 'foo.js' ), + line: 1, + column: 15 + }, + frame: ` + 1: export default function foo ( a, b ) { + ^ + 2: assert.equal( a, b ); + 3: return 3; + `, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#ambiguous-default-export` + } + ] }; diff --git a/test/function/warn-on-auto-named-default-exports/_config.js b/test/function/warn-on-auto-named-default-exports/_config.js index f093781..44e493b 100644 --- a/test/function/warn-on-auto-named-default-exports/_config.js +++ b/test/function/warn-on-auto-named-default-exports/_config.js @@ -1,10 +1,10 @@ -var assert = require( 'assert' ); - module.exports = { description: 'warns if default and named exports are used in auto mode', - warnings: function ( warnings ) { - assert.deepEqual( warnings, [ - 'Using named and default exports together. Consumers of your bundle will have to use bundle[\'default\'] to access the default export, which may not be what you want. Use `exports: \'named\'` to disable this warning. See https://github.com/rollup/rollup/wiki/JavaScript-API#exports for more information' - ]); - } + warnings: [ + { + code: 'MIXED_EXPORTS', + message: `Using named and default exports together. Consumers of your bundle will have to use bundle['default'] to access the default export, which may not be what you want. Use \`exports: 'named'\` to disable this warning`, + url: `https://github.com/rollup/rollup/wiki/JavaScript-API#exports` + } + ] }; diff --git a/test/function/warn-on-empty-bundle/_config.js b/test/function/warn-on-empty-bundle/_config.js index 2e599ad..a6d0a23 100644 --- a/test/function/warn-on-empty-bundle/_config.js +++ b/test/function/warn-on-empty-bundle/_config.js @@ -1,10 +1,9 @@ -const assert = require( 'assert' ); - module.exports = { description: 'warns if empty bundle is generated (#444)', - warnings: warnings => { - assert.deepEqual( warnings, [ - 'Generated an empty bundle' - ]); - } + warnings: [ + { + code: 'EMPTY_BUNDLE', + message: 'Generated an empty bundle' + } + ] }; diff --git a/test/function/warn-on-eval/_config.js b/test/function/warn-on-eval/_config.js index d6ac89f..a959bce 100644 --- a/test/function/warn-on-eval/_config.js +++ b/test/function/warn-on-eval/_config.js @@ -1,16 +1,20 @@ -var assert = require( 'assert' ); - -var warned = false; - module.exports = { description: 'warns about use of eval', - options: { - onwarn: function ( message ) { - warned = true; - assert.ok( /Use of `eval` \(in .+?main\.js\) is strongly discouraged, as it poses security risks and may cause issues with minification\. See https:\/\/github.com\/rollup\/rollup\/wiki\/Troubleshooting#avoiding-eval for more details/.test( message ) ); + warnings: [ + { + code: 'EVAL', + message: `Use of eval is strongly discouraged, as it poses security risks and may cause issues with minification`, + pos: 13, + loc: { + column: 13, + file: require( 'path' ).resolve( __dirname, 'main.js' ), + line: 1 + }, + frame: ` + 1: var result = eval( '1 + 1' ); + ^ + `, + url: 'https://github.com/rollup/rollup/wiki/Troubleshooting#avoiding-eval' } - }, - exports: function () { - assert.ok( warned, 'did not warn' ); - } + ] }; diff --git a/test/function/warn-on-namespace-conflict/_config.js b/test/function/warn-on-namespace-conflict/_config.js new file mode 100644 index 0000000..2054d75 --- /dev/null +++ b/test/function/warn-on-namespace-conflict/_config.js @@ -0,0 +1,9 @@ +module.exports = { + description: 'warns on duplicate export * from', + warnings: [ + { + code: 'NAMESPACE_CONFLICT', + message: `Conflicting namespaces: main.js re-exports 'foo' from both foo.js (will be ignored) and deep.js` + } + ] +}; diff --git a/test/function/double-named-export-from/bar.js b/test/function/warn-on-namespace-conflict/bar.js similarity index 100% rename from test/function/double-named-export-from/bar.js rename to test/function/warn-on-namespace-conflict/bar.js diff --git a/test/function/double-named-export-from/deep.js b/test/function/warn-on-namespace-conflict/deep.js similarity index 100% rename from test/function/double-named-export-from/deep.js rename to test/function/warn-on-namespace-conflict/deep.js diff --git a/test/function/double-named-export-from/foo.js b/test/function/warn-on-namespace-conflict/foo.js similarity index 100% rename from test/function/double-named-export-from/foo.js rename to test/function/warn-on-namespace-conflict/foo.js diff --git a/test/function/double-named-export-from/main.js b/test/function/warn-on-namespace-conflict/main.js similarity index 100% rename from test/function/double-named-export-from/main.js rename to test/function/warn-on-namespace-conflict/main.js diff --git a/test/function/warn-on-top-level-this/_config.js b/test/function/warn-on-top-level-this/_config.js index 99c3f2c..4d9033e 100644 --- a/test/function/warn-on-top-level-this/_config.js +++ b/test/function/warn-on-top-level-this/_config.js @@ -2,11 +2,25 @@ const assert = require( 'assert' ); module.exports = { description: 'warns on top-level this (#770)', - warnings: warnings => { - assert.deepEqual( warnings, [ - `main.js (3:1) The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten. See https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined for more information` - ]); - }, + warnings: [ + { + code: 'THIS_IS_UNDEFINED', + message: `The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten`, + pos: 81, + loc: { + file: require( 'path' ).resolve( __dirname, 'main.js' ), + line: 3, + column: 0 + }, + frame: ` + 1: const someVariableJustToCheckOnCorrectLineNumber = true; // eslint-disable-line + 2: + 3: this.foo = 'bar'; + ^ + `, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined` + } + ], runtimeError: err => { assert.equal( err.message, `Cannot set property 'foo' of undefined` ); } diff --git a/test/function/warn-on-unused-missing-imports/_config.js b/test/function/warn-on-unused-missing-imports/_config.js index 1fca512..08cc6ba 100644 --- a/test/function/warn-on-unused-missing-imports/_config.js +++ b/test/function/warn-on-unused-missing-imports/_config.js @@ -3,9 +3,22 @@ const assert = require( 'assert' ); module.exports = { description: 'warns on missing (but unused) imports', - warnings: warnings => { - assert.deepEqual( warnings, [ - `Non-existent export 'b' is imported from ${path.resolve(__dirname, 'foo.js')} by ${path.resolve(__dirname, 'main.js')}` - ]); - } + warnings: [ + { + code: 'NON_EXISTENT_EXPORT', + message: `Non-existent export 'b' is imported from foo.js`, + pos: 12, + loc: { + file: path.resolve( __dirname, 'main.js' ), + line: 1, + column: 12 + }, + frame: ` + 1: import { a, b } from './foo.js'; + ^ + 2: + 3: assert.equal( a, 42 ); + ` + } + ] }; diff --git a/test/sourcemaps/transform-without-sourcemap/_config.js b/test/sourcemaps/transform-without-sourcemap/_config.js index ad33354..0ce79c0 100644 --- a/test/sourcemaps/transform-without-sourcemap/_config.js +++ b/test/sourcemaps/transform-without-sourcemap/_config.js @@ -1,10 +1,5 @@ -const assert = require( 'assert' ); - -let warnings = []; - module.exports = { description: 'preserves sourcemap chains when transforming', - before: () => warnings = [], // reset options: { plugins: [ { @@ -13,14 +8,13 @@ module.exports = { return code; } } - ], - onwarn ( msg ) { - warnings.push( msg ); - } + ] }, - test: () => { - assert.deepEqual( warnings, [ - `Sourcemap is likely to be incorrect: a plugin ('fake plugin') was used to transform files, but didn't generate a sourcemap for the transformation. Consult https://github.com/rollup/rollup/wiki/Troubleshooting and the plugin documentation for more information` - ]); - } + warnings: [ + { + code: `SOURCEMAP_BROKEN`, + message: `Sourcemap is likely to be incorrect: a plugin ('fake plugin') was used to transform files, but didn't generate a sourcemap for the transformation. Consult the plugin documentation for help`, + url: `https://github.com/rollup/rollup/wiki/Troubleshooting#sourcemap-is-likely-to-be-incorrect` + } + ] }; diff --git a/test/test.js b/test/test.js index 50f63e8..789ac44 100644 --- a/test/test.js +++ b/test/test.js @@ -58,6 +58,27 @@ function loader ( modules ) { }; } +function compareWarnings ( actual, expected ) { + assert.deepEqual( + actual.map( warning => { + const clone = Object.assign( {}, warning ); + delete clone.toString; + + if ( clone.frame ) { + clone.frame = clone.frame.replace( /\s+$/gm, '' ); + } + + return clone; + }), + expected.map( warning => { + if ( warning.frame ) { + warning.frame = warning.frame.slice( 1 ).replace( /^\t+/gm, '' ).replace( /\s+$/gm, '' ).trim(); + } + return warning; + }) + ); +} + describe( 'rollup', function () { this.timeout( 10000 ); @@ -270,7 +291,11 @@ describe( 'rollup', function () { } if ( config.warnings ) { - config.warnings( warnings ); + if ( Array.isArray( config.warnings ) ) { + compareWarnings( warnings, config.warnings ); + } else { + config.warnings( warnings ); + } } else if ( warnings.length ) { throw new Error( `Got unexpected warnings:\n${warnings.join('\n')}` ); } @@ -377,11 +402,18 @@ describe( 'rollup', function () { const entry = path.resolve( SOURCEMAPS, dir, 'main.js' ); const dest = path.resolve( SOURCEMAPS, dir, '_actual/bundle' ); - const options = extend( {}, config.options, { entry }); + let warnings; + + const options = extend( {}, config.options, { + entry, + onwarn: warning => warnings.push( warning ) + }); PROFILES.forEach( profile => { ( config.skip ? it.skip : config.solo ? it.only : it )( 'generates ' + profile.format, () => { process.chdir( SOURCEMAPS + '/' + dir ); + warnings = []; + return rollup.rollup( options ).then( bundle => { const options = extend( {}, { format: profile.format, @@ -391,9 +423,16 @@ describe( 'rollup', function () { bundle.write( options ); - if ( config.before ) config.before(); - const result = bundle.generate( options ); - config.test( result.code, result.map ); + if ( config.test ) { + const { code, map } = bundle.generate( options ); + config.test( code, map ); + } + + if ( config.warnings ) { + compareWarnings( warnings, config.warnings ); + } else if ( warnings.length ) { + throw new Error( `Unexpected warnings` ); + } }); }); }); @@ -737,11 +776,9 @@ describe( 'rollup', function () { moduleName: 'myBundle' }); - assert.deepEqual( warnings, [ - `'util' is imported by entry, but could not be resolved – treating it as an external dependency. For help see https://github.com/rollup/rollup/wiki/Troubleshooting#treating-module-as-external-dependency`, - `Creating a browser bundle that depends on Node.js built-in module ('util'). You might need to include https://www.npmjs.com/package/rollup-plugin-node-builtins`, - `No name was provided for external module 'util' in options.globals – guessing 'util'` - ]); + const relevantWarnings = warnings.filter( warning => warning.code === 'MISSING_NODE_BUILTINS' ); + assert.equal( relevantWarnings.length, 1 ); + assert.equal( relevantWarnings[0].message, `Creating a browser bundle that depends on Node.js built-in module ('util'). You might need to include https://www.npmjs.com/package/rollup-plugin-node-builtins` ); }); }); });