From 6ee23a5fdde256db07def5ae6985f6e984b39ec7 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 4 Feb 2021 16:27:26 +0000 Subject: [PATCH] feat: use esbuild and playwright-test - remove webpack, karma and some babel packages - use esbuild to bundle - use playwright-test for browser tests --- cmds/test.js | 2 +- package.json | 26 +---- src/build/index.js | 70 +++++++------ src/config/babelrc.js | 51 --------- src/config/karma-entry.js | 13 --- src/config/karma.conf.js | 147 -------------------------- src/config/pw-test.js | 4 + src/config/register.js | 18 +++- src/config/user.js | 14 +-- src/config/webpack.config.js | 193 ----------------------------------- src/fixtures.browser.js | 4 +- src/test/browser.js | 41 ++++---- 12 files changed, 94 insertions(+), 489 deletions(-) delete mode 100644 src/config/babelrc.js delete mode 100644 src/config/karma-entry.js delete mode 100644 src/config/karma.conf.js create mode 100644 src/config/pw-test.js delete mode 100644 src/config/webpack.config.js diff --git a/cmds/test.js b/cmds/test.js index 6fdb97c9d..b061e6237 100644 --- a/cmds/test.js +++ b/cmds/test.js @@ -65,7 +65,7 @@ module.exports = { default: true }, colors: { - describe: 'Enable colors on output (only available in node runs)', + describe: 'Enable colors on output', type: 'boolean', default: true }, diff --git a/package.json b/package.json index a024282ad..11d54217c 100644 --- a/package.json +++ b/package.json @@ -51,14 +51,10 @@ }, "dependencies": { "@achingbrain/dependency-check": "^4.1.0", - "@babel/cli": "^7.12.10", - "@babel/core": "^7.12.10", - "@babel/plugin-transform-regenerator": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.10", + "@babel/core": "^7.12.13", + "@babel/preset-env": "^7.12.13", "@babel/preset-typescript": "^7.12.7", "@babel/register": "^7.12.10", - "@babel/runtime": "^7.12.5", "@commitlint/cli": "^11.0.0", "@commitlint/config-conventional": "^11.0.0", "@commitlint/lint": "^11.0.0", @@ -74,7 +70,6 @@ "@types/mocha": "^8.2.0", "@types/node": "^14.14.22", "@types/sinon": "^9.0.10", - "babel-loader": "^8.2.2", "buffer": "^6.0.3", "bytes": "^3.1.0", "camelcase": "^6.2.0", @@ -87,6 +82,7 @@ "cors": "^2.8.5", "dirty-chai": "^2.0.1", "electron-mocha": "^10.0.0", + "esbuild": "^0.8.39", "eslint": "^7.18.0", "eslint-config-ipfs": "^2.0.0", "execa": "^5.0.0", @@ -98,16 +94,6 @@ "globby": "^11.0.2", "ipfs-utils": "^6.0.0", "it-glob": "~0.0.10", - "json-loader": "~0.5.7", - "karma": "^6.0.3", - "karma-chrome-launcher": "^3.1.0", - "karma-cli": "^2.0.0", - "karma-firefox-launcher": "^2.1.0", - "karma-mocha": "^2.0.1", - "karma-mocha-reporter": "^2.2.5", - "karma-mocha-webworker": "^1.3.0", - "karma-sourcemap-loader": "~0.3.8", - "karma-webpack": "4.0.2", "lilconfig": "^2.0.2", "listr": "~0.14.2", "merge-options": "^3.0.4", @@ -117,6 +103,7 @@ "ora": "^5.3.0", "p-map": "^4.0.0", "pascalcase": "^1.0.0", + "playwright-test": "hugomrdias/playwright-test#feat/esbuild", "polka": "^0.5.2", "premove": "^3.0.1", "prompt-promise": "^1.0.3", @@ -125,14 +112,9 @@ "simple-git": "^2.28.0", "strip-bom": "^4.0.0", "strip-json-comments": "^3.1.1", - "terser-webpack-plugin": "^3.0.5", "typedoc": "^0.20.17", "typescript": "4.1.x", "update-notifier": "^5.0.0", - "webpack": "^4.43.0", - "webpack-bundle-analyzer": "^3.7.0", - "webpack-cli": "^3.3.10", - "webpack-merge": "^4.2.2", "yargs": "^16.2.0", "yargs-parser": "^20.2.3" }, diff --git a/src/build/index.js b/src/build/index.js index d9c0a13a4..5ec627807 100644 --- a/src/build/index.js +++ b/src/build/index.js @@ -1,14 +1,17 @@ /* eslint-disable no-console */ 'use strict' - +const esbuild = require('esbuild') const path = require('path') -const { readJsonSync } = require('fs-extra') +const pascalcase = require('pascalcase') const bytes = require('bytes') -const execa = require('execa') const { premove: del } = require('premove') -const { fromAegir, gzipSize, pkg, hasTsconfig } = require('./../utils') +const { gzipSize, pkg, hasTsconfig, fromRoot, paths } = require('./../utils') const tsCmd = require('../ts') const { userConfig } = require('../config/user') +const merge = require('merge-options').bind({ + ignoreUndefined: true, + concatArrays: true +}) /** * @typedef {import("../types").GlobalOptions} GlobalOptions @@ -21,47 +24,22 @@ const { userConfig } = require('../config/user') * @param {GlobalOptions & BuildOptions} argv */ module.exports = async (argv) => { - const input = argv._.slice(1) - const forwardOptions = argv['--'] ? argv['--'] : [] - const useBuiltinConfig = !forwardOptions.includes('--config') - const progress = !forwardOptions.includes('--progress') && !process.env.CI ? ['--progress'] : [] - const webpackConfig = useBuiltinConfig - ? ['--config', fromAegir('src/config/webpack.config.js')] - : [] - // Clean dist await del(path.join(process.cwd(), 'dist')) if (argv.bundle) { - // Run webpack - await execa('webpack-cli', [ - ...webpackConfig, - ...progress, - ...input, - ...forwardOptions - ], { - env: { - NODE_ENV: process.env.NODE_ENV || 'production', - AEGIR_BUILD_ANALYZE: argv.bundlesize ? 'true' : 'false', - AEGIR_NODE: argv.node ? 'true' : 'false', - AEGIR_TS: argv.tsRepo ? 'true' : 'false' - }, - localDir: path.join(__dirname, '../..'), - preferLocal: true, - stdio: 'inherit' - }) + const outfile = await build(argv) if (argv.bundlesize) { // @ts-ignore if (userConfig.bundlesize?.maxSize) { throw new Error('Config property `bundlesize.maxSize` is deprecated, use `build.bundlesizeMax`!') } - const stats = readJsonSync(path.join(process.cwd(), 'dist/stats.json')) - const gzip = await gzipSize(path.join(stats.outputPath, stats.assets[0].name)) + const gzip = await gzipSize(outfile) const maxsize = bytes(userConfig.build.bundlesizeMax) const diff = gzip - maxsize - console.log('Use http://webpack.github.io/analyse/ to load "./dist/stats.json".') + console.log('Use https://www.bundle-buddy.com/ to load "./dist/stats.json".') console.log(`Check previous sizes in https://bundlephobia.com/result?p=${pkg.name}@${pkg.version}`) if (diff > 0) { @@ -80,3 +58,31 @@ module.exports = async (argv) => { }) } } + +/** + * Build command + * + * @param {GlobalOptions & BuildOptions} argv + */ +const build = async (argv) => { + const outfile = path.join(paths.dist, 'index.js') + await esbuild.build(merge( + { + entryPoints: [fromRoot('src', argv.tsRepo ? 'index.ts' : 'index.js')], + bundle: true, + format: 'iife', + mainFields: ['browser', 'module', 'main'], + sourcemap: argv.bundlesize, + minify: true, + globalName: pascalcase(pkg.name), + metafile: argv.bundlesize ? path.join(paths.dist, 'stats.json') : undefined, + outfile, + define: { + 'process.env.NODE_ENV': '"production"' + } + }, + userConfig.build.config + )) + + return outfile +} diff --git a/src/config/babelrc.js b/src/config/babelrc.js deleted file mode 100644 index 65a73d48b..000000000 --- a/src/config/babelrc.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict' -const { pkg, browserslist } = require('./../utils') - -module.exports = function (opts = {}) { - const env = process.env.BABEL_ENV || process.env.NODE_ENV - const isEnvDevelopment = env === 'development' - const isEnvProduction = env === 'production' - const isEnvTest = env === 'test' - const isTSEnable = process.env.AEGIR_TS === 'true' - const targets = { browsers: pkg.browserslist || browserslist } - if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) { - throw new Error( - 'Using `babel-preset-env` requires that you specify `NODE_ENV` or ' + - '`BABEL_ENV` environment variables. Valid values are "development", ' + - '"test", and "production". Instead, received: ' + - JSON.stringify(env) + - '.' - ) - } - - return { - presets: [ - [ - require('@babel/preset-env').default, - { - corejs: 3, - useBuiltIns: 'entry', - modules: 'commonjs', - bugfixes: true, - targets - } - ], - isTSEnable && [ - '@babel/preset-typescript', - { - allowNamespaces: true - } - ] - ].filter(Boolean), - plugins: [ - '@babel/plugin-proposal-class-properties', - [ - require('@babel/plugin-transform-runtime').default, - { - helpers: false, - regenerator: true - } - ] - ] - } -} diff --git a/src/config/karma-entry.js b/src/config/karma-entry.js deleted file mode 100644 index 5525b5ae2..000000000 --- a/src/config/karma-entry.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' -/* eslint-disable */ -let testsContext -if (TS_ENABLED) { - testsContext = require.context(TEST_DIR, true, /\.spec\.ts$/) -} else { - testsContext = require.context(TEST_DIR, true, /\.spec\.js$/) -} - -if (TEST_BROWSER_JS) { - require(TEST_BROWSER_JS) -} -testsContext.keys().forEach(testsContext) diff --git a/src/config/karma.conf.js b/src/config/karma.conf.js deleted file mode 100644 index 2fe5ba5b1..000000000 --- a/src/config/karma.conf.js +++ /dev/null @@ -1,147 +0,0 @@ -'use strict' - -const merge = require('webpack-merge') -const webpack = require('webpack') -const path = require('path') -const webpackConfig = require('./webpack.config') -const { fromRoot, hasFile } = require('../utils') -const { userConfig } = require('./user') -const isTSEnable = process.env.AEGIR_TS === 'true' -const isWebworker = process.env.AEGIR_RUNNER === 'webworker' -const isProgressEnabled = process.env.AEGIR_PROGRESS === 'true' - -// Env to pass in the bundle with DefinePlugin -const env = { - TS_ENABLED: process.env.AEGIR_TS, - 'process.env': JSON.stringify(process.env), - TEST_DIR: JSON.stringify(fromRoot('test')), - TEST_BROWSER_JS: hasFile('test', isTSEnable ? 'browser.ts' : 'browser.js') - ? JSON.stringify(fromRoot('test', isTSEnable ? 'browser.ts' : 'browser.js')) - : JSON.stringify('') -} - -// Webpack overrides for karma -const karmaWebpackConfig = merge.strategy({ plugins: 'replace' })(webpackConfig(), { - entry: '', - plugins: [ - new webpack.DefinePlugin(env) - ], - module: { - rules: [ - { - oneOf: [ - { - test: /\.(js|ts)$/, - include: fromRoot('test'), - use: { - loader: require.resolve('babel-loader'), - options: { - presets: [require('./babelrc')()], - babelrc: false, - cacheDirectory: true - } - } - } - ] - } - ] - } -}) - -const karmaConfig = () => { - const files = JSON.parse(process.env.AEGIR_FILES) - const mocha = { - reporter: 'spec', - timeout: Number(process.env.AEGIR_MOCHA_TIMEOUT), - bail: process.env.AEGIR_MOCHA_BAIL === 'true', - grep: process.env.AEGIR_MOCHA_GREP - } - - const karmaEntry = path.join(__dirname, 'karma-entry.js') - - if (!files.length) { - // only try to load *.spec.js if we aren't specifying custom files - files.push(karmaEntry) - } - - return { - browsers: ['ChromeHeadlessNoSandbox'], - customLaunchers: { - ChromeHeadlessNoSandbox: { - base: 'ChromeHeadless', - flags: ['--no-sandbox'] - } - }, - frameworks: isWebworker ? ['mocha-webworker'] : ['mocha'], - basePath: process.cwd(), - files: files - .map(f => { - return { - pattern: f, - included: !isWebworker - } - }) - .concat([ - { - pattern: 'test/fixtures/**/*', - watched: false, - served: true, - included: false - } - ]), - - preprocessors: files.reduce((acc, f) => { - acc[f] = ['webpack', 'sourcemap'] - return acc - }, {}), - - client: { - mocha, - mochaWebWorker: { - pattern: [ - ...files, - 'karma-entry.js' - ], - mocha - } - }, - - webpack: karmaWebpackConfig, - - webpackMiddleware: { - stats: 'errors-only' - }, - - reporters: [ - isProgressEnabled && 'progress', - !isProgressEnabled && 'mocha' - ].filter(Boolean), - - mochaReporter: { - output: 'autowatch', - showDiff: true - }, - - plugins: [ - 'karma-chrome-launcher', - 'karma-firefox-launcher', - 'karma-mocha', - 'karma-mocha-reporter', - 'karma-mocha-webworker', - 'karma-sourcemap-loader', - 'karma-webpack' - ], - - autoWatch: false, - singleRun: true, - colors: true, - browserNoActivityTimeout: 50 * 1000 - } -} - -/** - * @param {any} config - */ -module.exports = (config) => { - config.set(merge(karmaConfig(), userConfig.karma)) -} diff --git a/src/config/pw-test.js b/src/config/pw-test.js new file mode 100644 index 000000000..aba9c50c7 --- /dev/null +++ b/src/config/pw-test.js @@ -0,0 +1,4 @@ +'use strict' +const { userConfig } = require('./user') + +module.exports = userConfig.test.browser.config || {} diff --git a/src/config/register.js b/src/config/register.js index 9ca2a01fb..6acc5ab23 100644 --- a/src/config/register.js +++ b/src/config/register.js @@ -1,7 +1,21 @@ 'use strict' -const { fromAegir } = require('./../utils') require('@babel/register')({ extensions: ['.ts'], - presets: [fromAegir('src/config/babelrc.js')] + presets: [ + [ + '@babel/preset-env', + { + modules: 'commonjs', + bugfixes: true, + targets: { node: true } + } + ], + [ + '@babel/preset-typescript', + { + allowNamespaces: true + } + ] + ] }) diff --git a/src/config/user.js b/src/config/user.js index 90944ff90..f11c65f68 100644 --- a/src/config/user.js +++ b/src/config/user.js @@ -3,7 +3,6 @@ const { lilconfigSync } = require('lilconfig') const merge = require('merge-options') -const utils = require('../utils') /** * @typedef {import("./../types").Options} Options @@ -71,17 +70,20 @@ const config = (searchFrom) => { debug: false, node: false, tsRepo: false, - // old options - webpack: {}, - karma: {}, hooks: {}, - entry: utils.fromRoot('src', 'index.js'), + // test cmd options + test: { + browser: { + config: {} + } + }, // build cmd options build: { bundle: true, bundlesize: false, bundlesizeMax: '100kB', - types: true + types: true, + config: {} }, // linter cmd options lint: { diff --git a/src/config/webpack.config.js b/src/config/webpack.config.js deleted file mode 100644 index b8b725d47..000000000 --- a/src/config/webpack.config.js +++ /dev/null @@ -1,193 +0,0 @@ -'use strict' - -const path = require('path') -const pascalcase = require('pascalcase') -const webpack = require('webpack') -const merge = require('webpack-merge') -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') -const TerserPlugin = require('terser-webpack-plugin') -const { fromRoot, pkg, paths } = require('../utils') -const { userConfig } = require('./user') -const isProduction = process.env.NODE_ENV === 'production' -const isTSEnable = process.env.AEGIR_TS === 'true' - -const base = (env, argv) => { - const filename = [ - 'index', - isProduction ? '.min' : null, - '.js' - ] - .filter(Boolean) - .join('') - - return { - bail: Boolean(isProduction), - mode: isProduction ? 'production' : 'development', - entry: [isTSEnable ? fromRoot('src', 'index.ts') : fromRoot('src', 'index.js')], - output: { - path: paths.dist, - filename: filename, - sourceMapFilename: filename + '.map', - library: pascalcase(pkg.name), - libraryTarget: 'umd', - globalObject: 'self', // Use `self` as `window` doesn't not exist within a Service/Web Worker context - devtoolModuleFilenameTemplate: info => 'file:' + encodeURI(info.absoluteResourcePath) - }, - module: { - rules: [ - { - oneOf: [ - { - test: /\.(js|ts)$/, - include: paths.src, - use: { - loader: require.resolve('babel-loader'), - options: { - presets: [require('./babelrc')()], - babelrc: false, - cacheDirectory: true - } - } - }, - { - test: /\.js$/, - exclude: /@babel(?:\/|\\{1,2})runtime/, - use: { - loader: require.resolve('babel-loader'), - options: { - presets: [require('./babelrc')()], - babelrc: false, - cacheDirectory: true, - sourceMaps: false - } - } - } - ] - } - ] - }, - resolve: { - extensions: ['.wasm', '.mjs', '.js', '.json', '.ts', '.d.ts'], - alias: { - '@babel/runtime': path.dirname( - require.resolve('@babel/runtime/package.json') - ) - } - }, - optimization: { - minimize: isProduction, - minimizer: [ - // This is only used in production mode - new TerserPlugin({ - terserOptions: { - parse: { - // we want terser to parse ecma 8 code. However, we don't want it - // to apply any minfication steps that turns valid ecma 5 code - // into invalid ecma 5 code. This is why the 'compress' and 'output' - // sections only apply transformations that are ecma 5 safe - // https://github.com/facebook/create-react-app/pull/4234 - ecma: 8 - }, - compress: { - ecma: 5, - warnings: false - }, - mangle: { - safari10: true - }, - output: { - ecma: 5, - comments: false - } - }, - // Use multi-process parallel running to improve the build speed - // Default number of concurrent runs: os.cpus().length - 1 - parallel: true, - // Enable file caching - cache: true, - sourceMap: true - }) - ] - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': JSON.stringify({ - DEBUG: process.env.DEBUG, - NODE_ENV: process.env.NODE_ENV - }) - }) - ], - target: 'web', - node: process.env.AEGIR_NODE === 'false' - ? { - global: true, - __filename: 'mock', - __dirname: 'mock', - dgram: false, - fs: false, - net: false, - tls: false, - child_process: false, - console: false, - // TODO remove this once readable-stream is fixed probably on in v4 - // https://github.com/nodejs/readable-stream/pull/435 - process: true, - Buffer: false, - setImmediate: false, - os: false, - assert: false, - constants: false, - events: false, - http: false, - path: false, - querystring: false, - stream: false, - string_decoder: false, - timers: false, - url: false, - util: false, - crypto: false - } - : { - dgram: 'empty', - fs: 'empty', - net: 'empty', - tls: 'empty', - child_process: 'empty', - console: false, - global: true, - process: true, - __filename: 'mock', - __dirname: 'mock', - Buffer: true, - setImmediate: true - }, - performance: { - hints: false - }, - stats: 'errors-warnings' - } -} - -module.exports = (env, argv) => { - const external = typeof userConfig.webpack === 'function' - ? userConfig.webpack(env, argv) - : userConfig.webpack - if (process.env.AEGIR_BUILD_ANALYZE === 'true') { - return merge( - base(env, argv), - { - plugins: [ - new BundleAnalyzerPlugin({ generateStatsFile: true, analyzerMode: 'static', logLevel: 'error', openAnalyzer: process.env.CI === undefined }) - ], - profile: true - }, - external - ) - } - - return merge( - base(env, argv), - external - ) -} diff --git a/src/fixtures.browser.js b/src/fixtures.browser.js index e2ba305dd..5973b7938 100644 --- a/src/fixtures.browser.js +++ b/src/fixtures.browser.js @@ -5,7 +5,7 @@ const { Buffer } = require('buffer') // note: filePath needs to be relative to the module root module.exports = function loadFixtures (filePath, module) { if (module) { - filePath = 'node_modules/' + module + '/' + filePath + filePath = module + '/' + filePath } return syncXhr(filePath) } @@ -13,7 +13,7 @@ module.exports = function loadFixtures (filePath, module) { // @dignifiedquire: I know this is considered bad practice (syncXhr), but it // makes testing life so much nicer! function syncXhr (filePath) { - const target = '/base/' + filePath + const target = filePath const request = new self.XMLHttpRequest() request.open('GET', target, false) diff --git a/src/test/browser.js b/src/test/browser.js index 04f50fbc2..d833267e8 100644 --- a/src/test/browser.js +++ b/src/test/browser.js @@ -7,37 +7,38 @@ const merge = require('merge-options') /** @typedef { import("execa").Options} ExecaOptions */ module.exports = (argv, execaOptions) => { - const input = argv._.slice(1) - const forwardOptions = argv['--'] ? argv['--'] : [] - const watch = argv.watch ? ['--auto-watch', '--no-single-run'] : [] - const verbose = argv.verbose ? ['--log-level', 'debug'] : ['--log-level', 'error'] - const colors = argv.colors ? ['--colors'] : [] + const extra = argv['--'] ? argv['--'] : [] + const forwardOptions = [ + ...extra, + argv.timeout && `--timeout=${argv.timeout}`, + argv.grep && `--grep=${argv.grep}`, + argv.bail && '--bail' + ].filter(Boolean) + const watch = argv.watch ? ['--watch'] : [] + const files = argv.files.length > 0 + ? [ + ...argv.files + ] + : [ + '**/*.spec.{js,ts}', + 'test/browser.{js,ts}' + ] return hook('browser', 'pre')(argv.userConfig) .then((hook = {}) => { - return execa('karma', + return execa('pw-test', [ - 'start', - fromAegir('src/config/karma.conf.js'), - ...colors, + ...files, + '--mode', argv.webworker ? 'worker' : 'main', ...watch, - ...verbose, - ...input, + '--config', fromAegir('src/config/pw-test.js'), ...forwardOptions ], merge( { env: { + AEGIR_RUNNER: argv.webworker ? 'worker' : 'main', NODE_ENV: process.env.NODE_ENV || 'test', - AEGIR_RUNNER: argv.webworker ? 'webworker' : 'browser', - AEGIR_NODE: argv.node, - AEGIR_TS: argv.tsRepo, - AEGIR_MOCHA_TIMEOUT: argv.timeout ? `${argv.timeout}` : '5000', - AEGIR_MOCHA_GREP: argv.grep, - AEGIR_MOCHA_BAIL: argv.bail ? 'true' : 'false', - AEGIR_PROGRESS: argv.progress ? 'true' : 'false', - AEGIR_FILES: JSON.stringify(argv.files), - IS_WEBPACK_BUILD: true, ...hook.env }, preferLocal: true,