diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 875e315a0a73..000000000000 --- a/.babelrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "presets": [ - ["@babel/preset-env", { - "modules": false, - "targets": { - "browsers": [ - "last 1 version", - "android >= 4.4", - "ie >= 10" - ] - } - }] - ] -} diff --git a/.eslintrc b/.eslintrc index 91966b6ce9d2..5b6301356b47 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,12 +6,8 @@ "browser": true, "builtin": true }, - "parserOptions": { - "ecmaVersion": 6 - }, "globals": { - "require": false, - "Promise": true + "require": false }, "rules": { "block-scoped-var": 2, diff --git a/.gitignore b/.gitignore index 492bbd5cf0b1..f21ffb033612 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ docs/doctrees build node_modules -plugins/combinations npm-debug.log scratch/ diff --git a/Gruntfile.js b/Gruntfile.js index 15378a4bfe54..211318bbd951 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2,96 +2,155 @@ module.exports = function(grunt) { var path = require('path'); var os = require('os'); + var through = require('through2'); + var proxyquire = require('proxyquireify'); + var versionify = require('browserify-versionify'); + var derequire = require('derequire/plugin'); + var collapser = require('bundle-collapser/plugin'); var excludedPlugins = ['react-native']; - var plugins = grunt.file.expand('plugins/*.js').filter(function(plugin) { + var plugins = grunt.option('plugins'); + // Create plugin paths and verify they exist + plugins = (plugins ? plugins.split(',') : []).map(function(plugin) { + var p = 'plugins/' + plugin + '.js'; + + if (!grunt.file.exists(p)) + throw new Error("Plugin '" + plugin + "' not found in plugins directory."); + + return p; + }); + + // custom browserify transformer to re-write plugins to + // self-register with Raven via addPlugin + function AddPluginBrowserifyTransformer() { + return function(file) { + return through(function(buf, enc, next) { + buf = buf.toString('utf8'); + if (/plugins/.test(file)) { + buf += "\nrequire('../src/singleton').addPlugin(module.exports);"; + } + this.push(buf); + next(); + }); + }; + } + + // Taken from http://dzone.com/snippets/calculate-all-combinations + var combine = function(a) { + var fn = function(n, src, got, all) { + if (n === 0) { + all.push(got); + return; + } + + for (var j = 0; j < src.length; j++) { + fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all); + } + }; + + var excluded = excludedPlugins.map(function(plugin) { + return 'plugins/' + plugin + '.js'; + }); + + // Remove the plugins that we don't want to build + a = a.filter(function(n) { + return excluded.indexOf(n) === -1; + }); + + var all = [a]; + + for (var i = 0; i < a.length; i++) { + fn(i, a, [], all); + } + + return all; + }; + + var plugins = grunt.file.expand('plugins/*.js'); + + var cleanedPlugins = plugins.filter(function(plugin) { var pluginName = path.basename(plugin, '.js'); return excludedPlugins.indexOf(pluginName) === -1; }); - // These files are generated with the 'generate:plugins-combined' npm script - var pluginCombinations = grunt.file.expand('plugins/combinations/*.js'); + var pluginSingleFiles = cleanedPlugins.map(function(plugin) { + var filename = path.basename(plugin); - var tests = grunt.file.expand('test/**/*.test.js'); + var file = {}; + file.src = plugin; + file.dest = path.join('build', 'plugins', filename); - var rollupConfig = { - core: { - options: [ - { - input: { - input: 'src/singleton.js' - }, - output: { - file: 'build/raven.js', - name: 'Raven', - banner: grunt.file.read('template/_copyright.js') - } - } - ] + return file; + }); + + var pluginCombinations = combine(plugins); + var pluginConcatFiles = pluginCombinations.reduce(function(dict, comb) { + var key = comb.map(function(plugin) { + return path.basename(plugin, '.js'); + }); + key.sort(); + + var dest = path.join('build/', key.join(','), '/raven.js'); + dict[dest] = ['src/singleton.js'].concat(comb); + + return dict; + }, {}); + + var browserifyConfig = { + options: { + banner: grunt.file.read('template/_copyright.js'), + browserifyOptions: { + standalone: 'Raven' // umd + }, + transform: [versionify], + plugin: [derequire, collapser] }, - plugins: { - options: [] + core: { + src: 'src/singleton.js', + dest: 'build/raven.js' }, - pluginCombinations: { - options: [] + 'plugins-combined': { + files: pluginConcatFiles, + options: { + transform: [[versionify], [new AddPluginBrowserifyTransformer()]] + } }, - tests: { - options: [] + test: { + src: 'test/**/*.test.js', + dest: 'build/raven.test.js', + options: { + browserifyOptions: { + debug: false // source maps + }, + ignore: ['react-native'], + plugin: [proxyquire.plugin] + } } }; - // Create a dedicated entry in rollup config for each individual - // plugin (each needs a unique `standalone` config) - plugins.forEach(function(plugin) { - var name = plugin + // Create a dedicated entry in browserify config for + // each individual plugin (each needs a unique `standalone` + // config) + var browserifyPluginTaskNames = []; + pluginSingleFiles.forEach(function(item) { + var name = item.src .replace(/.*\//, '') // everything before slash .replace('.js', ''); // extension var capsName = name.charAt(0).toUpperCase() + name.slice(1); var config = { - input: { - input: plugin - }, - output: { - file: path.join('build', 'plugins', path.basename(plugin)), - name: 'Raven.Plugins.' + capsName, - banner: grunt.file.read('template/_copyright.js') - } - }; - - rollupConfig.plugins.options.push(config); - }); - - // Create a dedicated entry in rollup config for each individual plugin combination - pluginCombinations.forEach(function(pluginCombination) { - var config = { - input: { - input: pluginCombination - }, - output: { - file: path.join('build', path.basename(pluginCombination, '.js'), 'raven.js'), - name: 'Raven', - banner: grunt.file.read('template/_copyright.js') - } - }; - - rollupConfig.pluginCombinations.options.push(config); - }); - - // Transpile all test scripts - tests.forEach(function(test) { - var config = { - input: { - input: test - }, - output: { - file: path.join('build', path.basename(test)), - name: path.basename(test, '.js') + src: item.src, + dest: item.dest, + options: { + browserifyOptions: { + // e.g. Raven.Plugins.Angular + standalone: 'Raven.Plugins.' + capsName + } } }; - - rollupConfig.tests.options.push(config); + browserifyConfig[name] = config; + browserifyPluginTaskNames.push('browserify:' + name); }); var awsConfigPath = path.join(os.homedir(), '.aws', 'raven-js.json'); @@ -99,9 +158,9 @@ module.exports = function(grunt) { pkg: grunt.file.readJSON('package.json'), aws: grunt.file.exists(awsConfigPath) ? grunt.file.readJSON(awsConfigPath) : {}, - clean: ['build', 'plugins/combinations'], + clean: ['build'], - rollup: rollupConfig, + browserify: browserifyConfig, uglify: { options: { @@ -218,30 +277,6 @@ module.exports = function(grunt) { grunt.initConfig(gruntConfig); // Custom Grunt tasks - grunt.registerMultiTask('rollup', 'Create the bundles', function() { - var build = require('./scripts/build'); - var options = this.options(); - var done = this.async(); - - var promises = Object.keys(options).map(function(key) { - return build(options[key].input, options[key].output); - }); - - Promise.all(promises) - .then(function() { - done(); - }) - ['catch'](function(error) { - grunt.fail.warn(error); - }); - }); - - grunt.registerTask('generate-plugin-combinations', function() { - var dest = './plugins/combinations'; - grunt.file.mkdir(dest); - require('./scripts/generate-plugin-combinations')(plugins, dest); - }); - grunt.registerTask('version', function() { var pkg = grunt.config.get('pkg'); @@ -280,28 +315,34 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-copy'); // 3rd party Grunt tasks + grunt.loadNpmTasks('grunt-browserify'); grunt.loadNpmTasks('grunt-release'); grunt.loadNpmTasks('grunt-s3'); grunt.loadNpmTasks('grunt-gitinfo'); grunt.loadNpmTasks('grunt-sri'); // Build tasks - grunt.registerTask('_prep', ['gitinfo', 'version']); - grunt.registerTask('build.test', ['_prep', 'rollup:core', 'rollup:tests']); - grunt.registerTask('build.core', ['_prep', 'rollup:core']); - grunt.registerTask('build.plugins', [ + grunt.registerTask('_prep', ['clean', 'gitinfo', 'version']); + grunt.registerTask( + 'browserify.core', + ['_prep', 'browserify:core'].concat(browserifyPluginTaskNames) + ); + grunt.registerTask('browserify.plugins-combined', [ '_prep', - 'generate-plugin-combinations', - 'rollup:plugins', - 'rollup:pluginCombinations', + 'browserify:plugins-combined' + ]); + grunt.registerTask('build.test', ['_prep', 'browserify.core', 'browserify:test']); + grunt.registerTask('build.core', ['browserify.core', 'uglify', 'sri:dist']); + grunt.registerTask('build.plugins-combined', [ + 'browserify.plugins-combined', + 'uglify', + 'sri:dist', 'sri:build' ]); - grunt.registerTask('build', ['build.core', 'build.plugins', 'uglify']); - - grunt.registerTask('dist', ['clean', 'build', 'copy:dist', 'sri:dist']); + grunt.registerTask('build', ['build.plugins-combined']); + grunt.registerTask('dist', ['build.core', 'copy:dist']); - // Test tasks - grunt.registerTask('test:ci', ['config:ci', 'build:test']); + grunt.registerTask('test:ci', ['config:ci', 'build.test']); // Webserver tasks grunt.registerTask('run:test', ['build.test', 'connect:test']); diff --git a/karma.unit.config.js b/karma.unit.config.js index 9a0c86a164c8..d6f468af2f3a 100644 --- a/karma.unit.config.js +++ b/karma.unit.config.js @@ -1,17 +1,6 @@ var commonConfig = require('./karma.config'); -var testFiles = [ - 'test/globals.js', - 'build/angular.test.js', - 'build/console.test.js', - 'build/json-stringify-safe.test.js', - 'build/raven.test.js', - 'build/react-native.test.js', - 'build/tracekit.test.js', - 'build/tracekit-parser.test.js', - 'build/utils.test.js', - 'build/vue.test.js', -]; +var testFiles = ['test/globals.js', 'build/raven.test.js']; module.exports = function(config) { var testConfig = Object.assign( diff --git a/package-lock.json b/package-lock.json index 6f102205ad1d..3a013cf303e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "raven-js", - "version": "3.21.0", + "version": "3.22.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index dc54e97146d5..85a14df01ccf 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,9 @@ "test:size": "grunt dist && bundlesize && git checkout -- dist/" }, "devDependencies": { - "@babel/core": "^7.0.0-beta.32", - "@babel/preset-env": "^7.0.0-beta.32", "bluebird": "^3.4.1", + "browserify-versionify": "^1.0.6", + "bundle-collapser": "^1.2.1", "bundlesize": "^0.15.2", "chai": "^4.1.1", "derequire": "2.0.3", @@ -40,6 +40,7 @@ "eslint": "^4.6.1", "eslint-config-prettier": "^2.3.0", "grunt": "^0.4.5", + "grunt-browserify": "^4.0.1", "grunt-cli": "^0.1.13", "grunt-contrib-clean": "^0.7.0", "grunt-contrib-connect": "^0.11.2", @@ -64,11 +65,8 @@ "mocha": "^3.5.0", "prettier": "^1.6.1", "proxyquireify": "^3.0.2", - "rollup": "^0.52.0", - "rollup-plugin-babel": "^4.0.0-beta.0", - "rollup-plugin-commonjs": "^8.2.6", - "rollup-plugin-node-resolve": "^3.0.0", "sinon": "^3.2.1", + "through2": "^2.0.0", "typescript": "^2.3.0", "whatwg-fetch": "^2.0.3" }, diff --git a/scripts/build.js b/scripts/build.js deleted file mode 100644 index 4c1b822281c0..000000000000 --- a/scripts/build.js +++ /dev/null @@ -1,40 +0,0 @@ -const rollup = require('rollup').rollup; -const babel = require('rollup-plugin-babel'); -const commonjs = require('rollup-plugin-commonjs'); // Remove this plugin as soon as all modules are ES6 -const resolve = require('rollup-plugin-node-resolve'); // Only needed for test build - -/** - * Build using rollup.js - * - * @see https://rollupjs.org/#javascript-api - * - * @param inputOptions - * @param outputOptions - * @returns Promise - */ -async function build(inputOptions, outputOptions) { - const input = Object.assign( - { - plugins: [ - commonjs(), // We can remove this plugin if there are no more CommonJS modules - resolve(), // We need this plugin only to build the test script - babel({ - exclude: 'node_modules/**' - }) - ] - }, - inputOptions - ); - - const output = Object.assign( - { - format: 'umd' - }, - outputOptions - ); - - const bundle = await rollup(input); - await bundle.write(output); -} - -module.exports = build; diff --git a/scripts/generate-plugin-combinations.js b/scripts/generate-plugin-combinations.js deleted file mode 100755 index 2b10e944081c..000000000000 --- a/scripts/generate-plugin-combinations.js +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -const template = plugins => `/* This file was generated by './scripts/generate-plugin-combinations.js'! */ -const Raven = require('../../src/singleton'); -${plugins.map(plugin => `const ${plugin}Plugin = require('../${plugin}');`).join('\n')} - -Raven -${plugins.map(plugin => ` .addPlugin(${plugin}Plugin)`).join('\n')}; -`; - -/** - * Taken from http://dzone.com/snippets/calculate-all-combinations - */ -function combine(a) { - const fn = function(n, src, got, all) { - if (n === 0) { - all.push(got); - return; - } - - for (let j = 0; j < src.length; j++) { - fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all); - } - }; - - const all = [a]; - - for (let i = 0; i < a.length; i++) { - fn(i, a, [], all); - } - - all.splice(1, 1); // Remove the empty array - - return all; -} - -/** - * Generate all plugin combinations. - * - * @param {array} plugins - * @param {string} dest - */ -function generate(plugins, dest) { - const pluginNames = plugins.map((plugin) => { - return path.basename(plugin, '.js'); - }); - - const pluginCombinations = combine(pluginNames); - - pluginCombinations.forEach((pluginCombination) => { - fs.writeFileSync( - path.resolve(dest, `${pluginCombination.join(',')}.js`), - template(pluginCombination), - ); - }); -} - -module.exports = generate; diff --git a/test/raven.test.js b/test/raven.test.js index 65d4604df844..0614a64399e0 100644 --- a/test/raven.test.js +++ b/test/raven.test.js @@ -6,13 +6,20 @@ var sinon = require('sinon'); var chai = require('chai'); var assert = chai.assert; -var _Raven = require('../src/raven'); -_Raven.prototype._getUuid = function() { - return 'abc123'; // Mock in order to get a predictable UUID -}; +var proxyquire = require('proxyquireify')(require); var TraceKit = require('../vendor/TraceKit/tracekit'); +var _Raven = proxyquire('../src/raven', { + // Ensure same TraceKit obj is shared (without specifying this, proxyquire + // seems to clone dependencies or something weird) + '../vendor/TraceKit/tracekit': TraceKit +}); + +_Raven.prototype._getUuid = function() { + return 'abc123'; +}; + var utils = require('../src/utils'); var joinRegExp = utils.joinRegExp; var supportsErrorEvent = utils.supportsErrorEvent; @@ -2503,7 +2510,7 @@ describe('Raven (public API)', function() { }, error); }); - it('should return input function as-is if accessing __raven__ prop throws exception', function() { + it('should return input funciton as-is if accessing __raven__ prop throws exception', function() { // see raven-js#495 var fn = function() {}; Object.defineProperty(fn, '__raven__', { @@ -2512,13 +2519,13 @@ describe('Raven (public API)', function() { } }); assert.throw(function() { - return fn.__raven__; + fn.__raven__; }, 'Permission denied'); var wrapped = Raven.wrap(fn); assert.equal(fn, wrapped); }); - it('should return input function as-is if accessing __raven_wrapper__ prop throws exception', function() { + it('should return input funciton as-is if accessing __raven_wrapper__ prop throws exception', function() { // see raven-js#495 var fn = function() {}; Object.defineProperty(fn, '__raven_wrapper__', { @@ -2527,7 +2534,7 @@ describe('Raven (public API)', function() { } }); assert.throw(function() { - return fn.__raven_wrapper__; + fn.__raven_wrapper__; }, 'Permission denied'); var wrapped = Raven.wrap(fn); assert.equal(fn, wrapped);