diff --git a/examples/example-typescript/package.json b/examples/example-typescript/package.json new file mode 100644 index 0000000..7718e99 --- /dev/null +++ b/examples/example-typescript/package.json @@ -0,0 +1,15 @@ +{ + "scripts": { + "clean": "shx rm -rf dist", + "start": "cross-env NODE_ENV=development node ../../lib/cli.js -c --hot --content-base public --port 9001", + "build": "npm run clean && cross-env NODE_ENV=production rollup -c" + }, + "devDependencies": { + "@rollup/plugin-typescript": "^8.2.0", + "cross-env": "^5.2.0", + "rollup": "^1.28.0", + "shx": "^0.3.2", + "tslib": "^2.1.0", + "typescript": "^4.2.2" + } +} diff --git a/examples/example-typescript/public/index.html b/examples/example-typescript/public/index.html new file mode 100644 index 0000000..33d8961 --- /dev/null +++ b/examples/example-typescript/public/index.html @@ -0,0 +1,12 @@ + + + + + + Typescript Test + + +
+ + + \ No newline at end of file diff --git a/examples/example-typescript/rollup.config.js b/examples/example-typescript/rollup.config.js new file mode 100644 index 0000000..6eadb61 --- /dev/null +++ b/examples/example-typescript/rollup.config.js @@ -0,0 +1,16 @@ +import typescript from '@rollup/plugin-typescript'; + +let config = { + input: './src/main.ts', + output: { + dir: 'dist', + format: 'esm', + entryFileNames: '[name].[hash].js', + assetFileNames: '[name].[hash][extname]' + }, + plugins: [ + typescript() + ] +} + +export default config; \ No newline at end of file diff --git a/examples/example-typescript/src/main.ts b/examples/example-typescript/src/main.ts new file mode 100644 index 0000000..c49bee4 --- /dev/null +++ b/examples/example-typescript/src/main.ts @@ -0,0 +1,7 @@ +function add (a: number, b: number) { + return a + b; +} + +let message: string = 'hello world'; + +document.body.textContent = message + ', Adding 1 + 2 = ' + add(1, 2); \ No newline at end of file diff --git a/lib/impl/PluginContext.js b/lib/impl/PluginContext.js index 735ac77..94fe916 100644 --- a/lib/impl/PluginContext.js +++ b/lib/impl/PluginContext.js @@ -137,7 +137,7 @@ module.exports = { * @param {string} e */ warn (e) { - console.warn(e); + container.__errorHandler.warn(e); }, /** diff --git a/lib/impl/PluginErrorHandler.js b/lib/impl/PluginErrorHandler.js index a9f7ab3..8256398 100644 --- a/lib/impl/PluginErrorHandler.js +++ b/lib/impl/PluginErrorHandler.js @@ -1,5 +1,37 @@ // @ts-check +// Formats the message as best it can. +// Note that this diverges from Rollup warnings, which are formatted for the CLI only. +// When using Rollup API by itself, it only prints normal warning message without all of the other properties like frame or position. +// Nollup however has a dev-server, so it cannot take the CLI approach. Instead regardless of using CLI/API, it will be formatted. +function format (error) { + let output = ''; + + if (typeof error === 'object') { + if (error.pluginCode) { + output += error.pluginCode + ': '; + } + + if (error.message) { + output += error.message; + } + + if (error.loc) { + output += '\n'; + output += error.loc.file.replace(process.cwd(), ''); + output += ` (${error.loc.line}:${error.loc.column})`; + } + + if (error.frame) { + output += '\n' + error.frame; + } + } else { + output += error; + } + + return { message: output }; +} + class PluginErrorHandler { /** * @param {function} callback @@ -12,18 +44,16 @@ class PluginErrorHandler { this.__errorThrown = false; } + warn (e) { + console.warn('\x1b[1m\x1b[33m' + format(e).message + '\x1b[39m\x1b[22m'); + } + /** * @param {object|string} e * @return {void|never} */ throw (e) { - if (typeof e === 'object' && e.frame) { - e.message = e.message + '\n' + e.frame; - } - - if (typeof e === 'string') { - e = { message: e }; - } + e = format(e); e.__isNollupError = true; diff --git a/test/cases/api/context.js b/test/cases/api/context.js index 0baea4f..e51857f 100644 --- a/test/cases/api/context.js +++ b/test/cases/api/context.js @@ -527,7 +527,92 @@ describe ('API: Plugin Context', () => { }); describe ('warn', () => { - it ('should output warning message'); + let _consoleLog = console.warn; + let _logged = []; + + beforeEach(() => { + console.warn = function (...args) { + _logged.push(args.join(' ').replace('\x1b[1m\x1b[33m', '').replace('\x1b[39m\x1b[22m', '')); + } + }); + + afterEach(() => { + console.warn = _consoleLog; + _logged = []; + }); + + it ('should output warning message', async () => { + fs.stub('./src/main.js', () => 'export default 123'); + + let bundle = await nollup({ + input: './src/main.js', + plugins: [{ + transform () { + this.warn('my warning'); + } + }] + }); + + await bundle.generate({ format: 'esm' }); + expect(_logged[0]).to.equal('my warning'); + fs.reset(); + }); + + it ('should allow message object', async () => { + fs.stub('./src/main.js', () => 'export default 123'); + + let bundle = await nollup({ + input: './src/main.js', + plugins: [{ + transform () { + this.warn({ message: 'my warning' }); + } + }] + }); + + await bundle.generate({ format: 'esm' }); + expect(_logged[0]).to.equal('my warning'); + fs.reset(); + }); + + it ('should allow message object with frame', async () => { + fs.stub('./src/main.js', () => 'export default 123'); + + let bundle = await nollup({ + input: './src/main.js', + plugins: [{ + transform () { + this.warn({ message: 'my warning', frame: ' var abc;\n ^'}); + } + }] + }); + + await bundle.generate({ format: 'esm' }); + expect(_logged[0]).to.equal('my warning\n var abc;\n ^'); + fs.reset(); + }); + + it ('should allow message object with pluginCode, message, loc and frame', async () => { + fs.stub('./src/main.js', () => 'export default 123'); + + let bundle = await nollup({ + input: './src/main.js', + plugins: [{ + transform (code, id) { + this.warn({ + message: 'my warning', + pluginCode: 'TS1234', + loc: { line: 1, column: 5, file: id }, + frame: ' var abc;\n ^' + }); + } + }] + }); + + await bundle.generate({ format: 'esm' }); + expect(_logged[0]).to.equal(`TS1234: my warning\n${path.resolve(process.cwd(), './src/main.js').replace(process.cwd(), '')} (1:5)\n var abc;\n ^`); + fs.reset(); + }); }); describe ('error', () => {