diff --git a/build/tasks/release-checks/suite-steps.js b/build/tasks/release-checks/suite-steps.js index 68ee7646c..2d726d259 100644 --- a/build/tasks/release-checks/suite-steps.js +++ b/build/tasks/release-checks/suite-steps.js @@ -65,6 +65,8 @@ module.exports = function(suite) { if (applicable(features, 'webpack')) { steps.push( + new tests.webpack.AuBuildDoesNotThrowCommandLineErrors(), + new tests.webpack.AuBuildStartsWebpackInWatchMode(), new tests.webpack.AuRunDoesNotThrowCommandLineErrors(), new tests.webpack.AuRunLaunchesServer(), new tests.webpack.AuRunRendersPage(), diff --git a/build/tasks/release-checks/tasks/check-javascript-errors.js b/build/tasks/release-checks/tasks/check-javascript-errors.js index b501dec38..3fec71766 100644 --- a/build/tasks/release-checks/tasks/check-javascript-errors.js +++ b/build/tasks/release-checks/tasks/check-javascript-errors.js @@ -1,6 +1,5 @@ const Task = require('./task'); const puppeteer = require('puppeteer'); -const { killProc } = require('../utils'); const PUPPETEER_TIMEOUT = 5000; module.exports = class CheckJavascriptErrors extends Task { @@ -42,11 +41,6 @@ module.exports = class CheckJavascriptErrors extends Task { }); } - stop() { - this.resolve(); - killProc(this.proc); - } - getTitle() { return super.getTitle() + ` (url: ${this.url})`; } diff --git a/build/tasks/release-checks/tasks/execute-command.js b/build/tasks/release-checks/tasks/execute-command.js index ded45c76d..aa5101e33 100644 --- a/build/tasks/release-checks/tasks/execute-command.js +++ b/build/tasks/release-checks/tasks/execute-command.js @@ -1,5 +1,4 @@ const Task = require('./task'); -const { killProc } = require('../utils'); const { spawn } = require('child_process'); const os = require('os'); @@ -24,8 +23,9 @@ module.exports = class ExecuteCommand extends Task { execute() { this.proc = spawn(this.command, this.parameters); - this.promise = new Promise((resolve) => { + this.promise = new Promise((resolve, reject) => { this.resolve = resolve; + this.reject = reject; }); this.proc.stdout.on('data', (data) => { @@ -62,11 +62,6 @@ module.exports = class ExecuteCommand extends Task { return this.execute(); } - stop() { - this.resolve(); - killProc(this.proc); - } - getTitle() { return super.getTitle() + ` (command: ${this.command} ${this.parameters.join(' ')})`; } diff --git a/build/tasks/release-checks/tasks/take-screenshot-of-page.js b/build/tasks/release-checks/tasks/take-screenshot-of-page.js index 2d9e3132a..16fc364f5 100644 --- a/build/tasks/release-checks/tasks/take-screenshot-of-page.js +++ b/build/tasks/release-checks/tasks/take-screenshot-of-page.js @@ -1,6 +1,5 @@ const Task = require('./task'); const puppeteer = require('puppeteer'); -const { killProc } = require('../utils'); const PUPPETEER_TIMEOUT = 5000; module.exports = class TakeScreenShotOfPage extends Task { @@ -38,11 +37,6 @@ module.exports = class TakeScreenShotOfPage extends Task { }); } - stop() { - this.resolve(); - killProc(this.proc); - } - getTitle() { return super.getTitle() + ` (url: ${this.url})`; } diff --git a/build/tasks/release-checks/tasks/task.js b/build/tasks/release-checks/tasks/task.js index a81e8552b..17ab408ef 100644 --- a/build/tasks/release-checks/tasks/task.js +++ b/build/tasks/release-checks/tasks/task.js @@ -1,4 +1,5 @@ const Step = require('../step'); +const kill = require('tree-kill'); module.exports = class Task extends Step { constructor(title) { @@ -13,4 +14,14 @@ module.exports = class Task extends Step { getTitle() { return `[TASK] ${this.title}`; } + + stop() { + kill(this.proc.pid, err => { + if (err) { + this.reject && this.reject(err); + } else { + this.resolve && this.resolve(); + } + }); + } }; diff --git a/build/tasks/release-checks/tests/webpack/au-build.js b/build/tasks/release-checks/tests/webpack/au-build.js new file mode 100644 index 000000000..1cf479faa --- /dev/null +++ b/build/tasks/release-checks/tests/webpack/au-build.js @@ -0,0 +1,64 @@ +const Test = require('../test'); +const ExecuteCommand = require('../../tasks/execute-command'); +const path = require('path'); +const fs = require('fs'); + +class AuBuildDoesNotThrowCommandLineErrors extends Test { + constructor() { + super('au build does not throw commandline errors'); + } + + onOutput(message) { + this.debug(message); + + if (message.toLowerCase().indexOf('error') > -1) { + this.executeCommand.stop(); + this.fail(); + } else if (isBuildCompletedMessage(message)) { + this.success(); + this.executeCommand.stop(); + } + } + + execute() { + this.executeCommand = new ExecuteCommand('au', ['build'], (msg) => this.onOutput(msg)); + return this.executeCommand.executeAsNodeScript(); + } +} + +class AuBuildStartsWebpackInWatchMode extends Test { + constructor(fileToChange) { + super('au build --watch starts webpack in watch mode'); + + this.fileToChange = fileToChange || path.join('src', 'app.html'); + this.firstBuildCompleted = false; + } + + onOutput(message) { + this.debug(message); + + if (message.toLowerCase().indexOf('error') > -1) { + this.executeCommand.stop(); + this.fail(); + } else if (message.indexOf('webpack is watching the files') > -1) { + this.success(); + this.executeCommand.stop(); + } + } + + execute(context) { + this.context = context; + + this.executeCommand = new ExecuteCommand('au', ['build', '--watch'], (msg) => this.onOutput(msg)); + return this.executeCommand.executeAsNodeScript(); + } +} + +function isBuildCompletedMessage(msg) { + return msg.indexOf('Built at') > -1; +} + +module.exports = { + AuBuildDoesNotThrowCommandLineErrors, + AuBuildStartsWebpackInWatchMode +}; diff --git a/build/tasks/release-checks/tests/webpack/index.js b/build/tasks/release-checks/tests/webpack/index.js index 1d098164b..86c3f4289 100644 --- a/build/tasks/release-checks/tests/webpack/index.js +++ b/build/tasks/release-checks/tests/webpack/index.js @@ -1,3 +1,4 @@ module.exports = { - ...require('./au-run') + ...require('./au-run'), + ...require('./au-build') }; diff --git a/build/tasks/release-checks/utils.js b/build/tasks/release-checks/utils.js deleted file mode 100644 index 94a2c79fe..000000000 --- a/build/tasks/release-checks/utils.js +++ /dev/null @@ -1,16 +0,0 @@ -const os = require('os'); -const childProcess = require('child_process'); - -function killProc(proc) { - if (os.platform() === 'win32') { - childProcess.exec('taskkill /pid ' + proc.pid + ' /T /F'); - } else { - proc.stdin.pause(); - proc.kill(); - } -} - -module.exports = { - killProc -}; - diff --git a/lib/build/webpack-reporter.js b/lib/build/webpack-reporter.js index 5971a15a6..47d1dfa9d 100644 --- a/lib/build/webpack-reporter.js +++ b/lib/build/webpack-reporter.js @@ -3,7 +3,6 @@ module.exports = function reportReadiness(options) { const yargs = require('yargs'); const argv = yargs.argv; argv.color = require('supports-color'); - const open = require('open'); const useColor = argv.color; let startSentence = `Project is running at ${colorInfo(useColor, uri)}`; @@ -23,12 +22,6 @@ module.exports = function reportReadiness(options) { if (options.historyApiFallback) { console.log(`404s will fallback to ${colorInfo(useColor, options.historyApiFallback.index || '/index.html')}`); } - - if (options.open) { - open(uri).catch(function() { - console.log('Unable to open browser. If you are running in a headless environment, please do not use the open flag.'); - }); - } }; function createDomain(opts) { diff --git a/lib/dependencies.json b/lib/dependencies.json index a6244e0bb..f7ab0e8dd 100644 --- a/lib/dependencies.json +++ b/lib/dependencies.json @@ -4,6 +4,7 @@ "@types/lodash": "^4.14.117", "@types/node": "^10.11.6", "@types/webpack": "^4.4.15", + "app-settings-loader": "^1.0.2", "aspnet-webpack": "^3.0.0", "aurelia-animator-css": "^1.0.4", "aurelia-bootstrapper": "^2.3.2", diff --git a/lib/index.ts b/lib/index.ts index 07d6d0e49..ef1afe2e1 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -8,3 +8,5 @@ exports.ProjectItem = require('./project-item').ProjectItem; exports.build = require('./build'); exports.Configuration = require('./configuration').Configuration; exports.reportWebpackReadiness = require('./build/webpack-reporter'); +exports.NPM = require('./package-managers/npm').NPM; +exports.Yarn = require('./package-managers/yarn').Yarn; diff --git a/lib/package-managers/base-package-manager.js b/lib/package-managers/base-package-manager.js index 91d4d9a20..bc7534b9d 100644 --- a/lib/package-managers/base-package-manager.js +++ b/lib/package-managers/base-package-manager.js @@ -7,10 +7,14 @@ exports.BasePackageManager = class { } install(packages = [], workingDirectory = process.cwd(), command = 'install') { + return this.run(command, packages, workingDirectory); + } + + run(command, args = [], workingDirectory = process.cwd()) { return new Promise((resolve, reject) => { spawn( this.getExecutablePath(workingDirectory), - [command, ...packages], + [command, ...args], { stdio: 'inherit', cwd: workingDirectory } ) .on('close', resolve) diff --git a/package-lock.json b/package-lock.json index 4a12ee0a0..823af3dc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8804,6 +8804,12 @@ "nopt": "~1.0.10" } }, + "tree-kill": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", + "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "dev": true + }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", diff --git a/package.json b/package.json index 4c50d9a5f..07802a88f 100644 --- a/package.json +++ b/package.json @@ -61,12 +61,11 @@ "gulp": "^4.0.2", "htmlparser2": "^3.10.1", "lead": "^1.0.0", - "lodash": "^4.17.15", + "lodash": "^4.17.14", "map-stream": "0.0.7", "mkdirp": "^0.5.1", "node-libs-browser": "^2.2.1", "npm-which": "^3.0.1", - "open": "^6.4.0", "preprocess": "^3.1.0", "pumpify": "^2.0.0", "querystring-es3": "1.0.0-0", @@ -96,6 +95,7 @@ "nyc": "^14.1.1", "puppeteer": "^1.19.0", "rimraf": "^2.6.3", + "tree-kill": "^1.2.1", "ts-node": "^8.3.0", "typescript": "^3.5.3", "yargs": "^13.3.0" diff --git a/skeleton/cli-bundler/README.md b/skeleton/cli-bundler/README.md index 5acea7e07..d3b9daae9 100644 --- a/skeleton/cli-bundler/README.md +++ b/skeleton/cli-bundler/README.md @@ -8,10 +8,14 @@ To open browser automatically, do `au run --open`. To change dev server port, do `au run --port 8888`. +To change dev server host, do `au run --host 127.0.0.1` + // @if feat.babel To install new npm packages automatically, do `au run --auto-install` // @endif +**PS:** You could mix all the flags as well, `au run --host 127.0.0.1 --port 7070 --open` + // @if !feat.plugin ## Build for production diff --git a/skeleton/cli-bundler/aurelia_project/aurelia.json b/skeleton/cli-bundler/aurelia_project/aurelia.json index 8f8587fc9..a8a80ec71 100644 --- a/skeleton/cli-bundler/aurelia_project/aurelia.json +++ b/skeleton/cli-bundler/aurelia_project/aurelia.json @@ -94,6 +94,8 @@ "platform": { // @if feat.web "port": 9000, + "host": "localhost", + "open": false, "index": "index.html", "baseDir": ".", "output": "scripts", @@ -101,6 +103,7 @@ // @if feat['dotnet-core'] "port": 5000, + "host": "localhost", "index": "wwwroot/index.html", "baseDir": "./wwwroot", "baseUrl": "scripts", diff --git a/skeleton/cli-bundler/aurelia_project/tasks/run.ext b/skeleton/cli-bundler/aurelia_project/tasks/run.ext index 8aa3b915f..d69af564b 100644 --- a/skeleton/cli-bundler/aurelia_project/tasks/run.ext +++ b/skeleton/cli-bundler/aurelia_project/tasks/run.ext @@ -14,13 +14,17 @@ import {CLIOptions} from 'aurelia-cli'; import build from './build'; import watch from './watch'; +// recommended: https://www.browsersync.io/docs/api#api-create +const bs = browserSync.create(); + let serve = gulp.series( build, done => { - browserSync({ + bs.init({ online: false, - open: CLIOptions.hasFlag('open'), + open: CLIOptions.hasFlag('open') || project.platform.open, port: CLIOptions.getFlagValue('port') || project.platform.port, + host: CLIOptions.getFlagValue('host') || project.platform.host || "localhost", logLevel: 'silent', server: { baseDir: [project.platform.baseDir], @@ -32,6 +36,12 @@ let serve = gulp.series( }, function (err, bs) { if (err) return done(err); let urls = bs.options.get('urls').toJS(); + let host = bs.options.get('host'); + let port = bs.options.get('port'); + + if( host !== "localhost" ) + log(`Application Available At: http://${host}:${port}`); + log(`Application Available At: ${urls.local}`); log(`BrowserSync Available At: ${urls.ui}`); done(); @@ -48,9 +58,17 @@ function reload() { browserSync.reload(); } -let run = gulp.series( +const run = gulp.series( serve, done => { watch(reload); done(); } ); -export default run; +const shutdownAppServer = (code) =>{ + return new Promise(resolve => { + bs.exit(); + process.exit(code || 0); + resolve(); + }); +}; + +export { run as default , shutdownAppServer }; diff --git a/skeleton/cli-bundler/aurelia_project/tasks/run.json b/skeleton/cli-bundler/aurelia_project/tasks/run.json index 9b51ac57e..9de7be11d 100644 --- a/skeleton/cli-bundler/aurelia_project/tasks/run.json +++ b/skeleton/cli-bundler/aurelia_project/tasks/run.json @@ -19,6 +19,11 @@ "type": "boolean" }, // @endif + { + "name": "host", + "description": "Set host address of the dev server, the accessible URL", + "type": "string" + }, { "name": "port", "description": "Set port number of the dev server", diff --git a/skeleton/common/aurelia_project/environments/dev.ext b/skeleton/common/aurelia_project/environments__if_not_webpack/dev.ext similarity index 100% rename from skeleton/common/aurelia_project/environments/dev.ext rename to skeleton/common/aurelia_project/environments__if_not_webpack/dev.ext diff --git a/skeleton/common/aurelia_project/environments/prod.ext b/skeleton/common/aurelia_project/environments__if_not_webpack/prod.ext similarity index 100% rename from skeleton/common/aurelia_project/environments/prod.ext rename to skeleton/common/aurelia_project/environments__if_not_webpack/prod.ext diff --git a/skeleton/common/aurelia_project/environments/stage.ext b/skeleton/common/aurelia_project/environments__if_not_webpack/stage.ext similarity index 100% rename from skeleton/common/aurelia_project/environments/stage.ext rename to skeleton/common/aurelia_project/environments__if_not_webpack/stage.ext diff --git a/skeleton/common/config/environment.json b/skeleton/common/config/environment.json new file mode 100644 index 000000000..a32129fd0 --- /dev/null +++ b/skeleton/common/config/environment.json @@ -0,0 +1,4 @@ +{ + "debug": true, + "testing": true +} \ No newline at end of file diff --git a/skeleton/common/config/environment.production.json b/skeleton/common/config/environment.production.json new file mode 100644 index 000000000..9ddbb4430 --- /dev/null +++ b/skeleton/common/config/environment.production.json @@ -0,0 +1,4 @@ +{ + "debug": false, + "testing": false +} \ No newline at end of file diff --git a/skeleton/common/src/environment.ext__if_not_plugin b/skeleton/common/src/environment.ext__if_not_plugin_and_not_webpack similarity index 100% rename from skeleton/common/src/environment.ext__if_not_plugin rename to skeleton/common/src/environment.ext__if_not_plugin_and_not_webpack diff --git a/skeleton/common/tsconfig.json__if_typescript b/skeleton/common/tsconfig.json__if_typescript index fadcfe313..18cfbb56c 100644 --- a/skeleton/common/tsconfig.json__if_typescript +++ b/skeleton/common/tsconfig.json__if_typescript @@ -32,10 +32,8 @@ "types": ["node", "jest"], // @endif - // @if feat.webpack && feat['postcss-typical'] && feat.protractor "typeRoots": ["./node_modules/@types"], - // @endif - + "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, @@ -44,6 +42,7 @@ "lib": ["es2015", "dom"], "moduleResolution": "node", "baseUrl": "src", + "resolveJsonModule": true, // @if feat.plugin "paths": { "resources": [ "" ] }, diff --git a/skeleton/cypress/README.md b/skeleton/cypress/README.md index aee065042..716fb793f 100644 --- a/skeleton/cypress/README.md +++ b/skeleton/cypress/README.md @@ -7,3 +7,12 @@ First, run `au run` and keep it running. Then run `au cypress` to run cypress in interactive mode. To perform a test-run and reports the results, do `au cypress --run`. + +To ask the `cypress` to start the application first and then start testing: `au cypress --start` + +The two following flags are useful when using `--start` flag: + * To change dev server port, do `au cypress --start --port 8888`. + * To change dev server host, do `au cypress --start --host 127.0.0.1` + + +**PS:** It is also possible to mix the flags `au cypress --run --start --port 7070 --host 127.0.0.1` diff --git a/skeleton/cypress/aurelia_project/tasks/cypress.ext b/skeleton/cypress/aurelia_project/tasks/cypress.ext index 847f2d5d6..472a991af 100644 --- a/skeleton/cypress/aurelia_project/tasks/cypress.ext +++ b/skeleton/cypress/aurelia_project/tasks/cypress.ext @@ -7,14 +7,37 @@ import * as cypress from 'cypress'; import * as config from '../../cypress.config'; // @endif import { CLIOptions } from 'aurelia-cli'; +import * as project from '../aurelia.json'; +import { default as runAppServer, shutdownAppServer } from './run'; -export default (cb) => { +const runCypress = (cb) => { if (CLIOptions.hasFlag('run')) { + const port = CLIOptions.getFlagValue('port') || project.platform.port; cypress .run(config) - .then(results => (results.totalFailed === 0 ? cb() : cb('Run failed!'))) + .then(results => { + if (results.totalFailed === 0) { + shutdownAppServer().then(cb); + } + else { + shutdownAppServer(1) + .then(_ => { + cb('Tests failed'); + }); + } + }) .catch(cb); } else { cypress.open(config); } +} + +export default (cb) => { + if (CLIOptions.hasFlag('start')) { + runCypress(cb); + runAppServer(); + return; + } + + runCypress(cb); }; diff --git a/skeleton/cypress/aurelia_project/tasks/cypress.json b/skeleton/cypress/aurelia_project/tasks/cypress.json index f63935ded..822e4f05f 100644 --- a/skeleton/cypress/aurelia_project/tasks/cypress.json +++ b/skeleton/cypress/aurelia_project/tasks/cypress.json @@ -4,7 +4,22 @@ "flags": [ { "name": "run", - "description": "Perfom a testrun and reports the results.", + "description": "Perform a test run and reports the results.", + "type": "boolean" + }, + { + "name": "host", + "description": "Set host address of the dev server, the accessible URL", + "type": "string" + }, + { + "name": "port", + "description": "Set port number of the dev server", + "type": "string" + }, + { + "name": "start", + "description": "Runs the application before starting to perform a testrun", "type": "boolean" } ] diff --git a/skeleton/cypress/cypress.config.js b/skeleton/cypress/cypress.config.js index 16c6054a0..a7943ea29 100644 --- a/skeleton/cypress/cypress.config.js +++ b/skeleton/cypress/cypress.config.js @@ -1,9 +1,11 @@ +const CLIOptions = require( 'aurelia-cli').CLIOptions; const aureliaConfig = require('./aurelia_project/aurelia.json'); -const port = aureliaConfig.platform.port; +const PORT = CLIOptions.getFlagValue('port') || aureliaConfig.platform.port; +const HOST = CLIOptions.getFlagValue('host') || aureliaConfig.platform.host; module.exports = { config: { - baseUrl: `http://localhost:${port}`, + baseUrl: `http://${HOST}:${PORT}`, fixturesFolder: 'test/e2e/fixtures', integrationFolder: 'test/e2e/integration', pluginsFile: 'test/e2e/plugins/index.js', diff --git a/skeleton/cypress/package.json b/skeleton/cypress/package.json index 9f3be9ec3..a3786ed50 100644 --- a/skeleton/cypress/package.json +++ b/skeleton/cypress/package.json @@ -1,4 +1,8 @@ { + "scripts": { + "e2e": "au cypress", + "e2e:headless": "au cypress --run --start" + }, "devDependencies": { // @if feat.typescript "@cypress/webpack-preprocessor": "", @@ -7,4 +11,4 @@ // @endif "cypress": "" } -} \ No newline at end of file +} diff --git a/skeleton/karma/karma.conf.js__if_cli-bundler b/skeleton/karma/karma.conf.js__if_cli-bundler index 4128ca649..4a160fd53 100644 --- a/skeleton/karma/karma.conf.js__if_cli-bundler +++ b/skeleton/karma/karma.conf.js__if_cli-bundler @@ -51,8 +51,39 @@ module.exports = function(config) { colors: true, logLevel: config.LOG_INFO, autoWatch: true, - browsers: ['Chrome'], - singleRun: false, + /* + * start these browsers + * available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + */ + // browsers: [ + // 'Chrome', + // ], + /* + * To run in non-headless mode: + * 1. Comment the following lines + * 2. Uncomment the above "browsers" setting + */ + browsers: [ + 'ChromeHeadless', + ], + customLaunchers: { + 'ChromeHeadless': { + base: 'Chrome', + flags: [ + '--no-sandbox', + '--headless', + '--disable-gpu', + '--remote-debugging-port=9222' + ] + } + }, + /** **************************************************************************** */ + + /* + * Continuous Integration mode + * if true, Karma captures browsers, runs the tests and exits + */ + singleRun: true, // client.args must be a array of string. // Leave 'aurelia-root', project.paths.root in this order so we can find // the root of the aurelia project. diff --git a/skeleton/karma/test__if_webpack/karma.conf.js b/skeleton/karma/test__if_webpack/karma.conf.js index 98213c149..fbdb24c67 100644 --- a/skeleton/karma/test__if_webpack/karma.conf.js +++ b/skeleton/karma/test__if_webpack/karma.conf.js @@ -30,7 +30,7 @@ module.exports = function (config) { 'test/karma-bundle.js': [ 'webpack' ] }, - webpack: require('../webpack.config')({ coverage: true, karma: true }), + webpack: require('../webpack.config')({ tests: true }), /* * test results reporter to use @@ -80,14 +80,35 @@ module.exports = function (config) { * start these browsers * available browser launchers: https://npmjs.org/browse/keyword/karma-launcher */ + // browsers: [ + // 'Chrome', + // ], + /* + * To run in non-headless mode: + * 1. Comment the following lines + * 2. Uncomment the above "browsers" setting + */ browsers: [ - 'Chrome', + 'ChromeHeadless', ], + customLaunchers: { + 'ChromeHeadless': { + base: 'Chrome', + flags: [ + '--no-sandbox', + '--headless', + '--disable-gpu', + '--remote-debugging-port=9222' + ] + } + }, + /** **************************************************************************** */ + /* * Continuous Integration mode * if true, Karma captures browsers, runs the tests and exits */ singleRun: true }) -} \ No newline at end of file +} diff --git a/skeleton/protractor/README.md b/skeleton/protractor/README.md index ced1972d9..b50ac9687 100644 --- a/skeleton/protractor/README.md +++ b/skeleton/protractor/README.md @@ -5,3 +5,14 @@ You need the app running for integration test. First, run `au run` and keep it running. Then run `au protractor`. + +To perform a test-run and reports the results, do `au protractor --run`. + +To ask the `protractor` to start the application first and then start testing: `au protractor --start` + +The two following flags are useful when using `--start` flag: + * To change dev server port, do `au protractor --start --port 8888`. + * To change dev server host, do `au protractor --start --host 127.0.0.1` + + +**PS:** It is also possible to mix the flags `au protractor --run --start --port 7070 --host 127.0.0.1` diff --git a/skeleton/protractor/aurelia_project/tasks/protractor.ext b/skeleton/protractor/aurelia_project/tasks/protractor.ext index 6d0d5244a..225473042 100644 --- a/skeleton/protractor/aurelia_project/tasks/protractor.ext +++ b/skeleton/protractor/aurelia_project/tasks/protractor.ext @@ -6,22 +6,36 @@ import * as gulp from 'gulp'; // @endif const {protractor, webdriver_update } = require('gulp-protractor'); -gulp.task('webdriver_update', webdriver_update); +import { CLIOptions } from 'aurelia-cli'; +import { default as runAppServer, shutdownAppServer } from './run'; -gulp.task('protractor', (cb) => { +function runApp(cb) { + if (CLIOptions.hasFlag('start')) { + runAppServer(); + } + cb(); +} + +function runProtractor(cb) { // @if feat.babel - gulp.src('test/e2e/**/*.e2e.js') + return gulp.src('test/e2e/**/*.e2e.js') // @endif // @if feat.typescript - gulp.src('test/e2e/**/*.e2e.ts') + return gulp.src('test/e2e/**/*.e2e.ts') // @endif - .pipe(protractor({configFile: 'test/protractor.conf.js'})) - .on('error', cb) - .on('end', cb); -}); + .pipe(protractor({ configFile: 'protractor.conf.js', args: process.argv.slice(3) })) + .on('end', () => { + shutdownAppServer().then(cb); + }) + .on('error', () => { + shutdownAppServer(1).then(cb); + }); +} // Setting up the test task export default gulp.series( - 'webdriver_update', - 'protractor' + runApp, + webdriver_update, + runProtractor ); + diff --git a/skeleton/protractor/aurelia_project/tasks/protractor.json b/skeleton/protractor/aurelia_project/tasks/protractor.json index 9d873a9f7..47f8df55d 100644 --- a/skeleton/protractor/aurelia_project/tasks/protractor.json +++ b/skeleton/protractor/aurelia_project/tasks/protractor.json @@ -1,4 +1,25 @@ { "name": "protractor", - "description": "Runs Protractor and reports the results. Start the application before running this task." + "description": "Runs Protractor and reports the results. Start the application before running this task.", + "flags": [{ + "name": "run", + "description": "Perform a test run and reports the results.", + "type": "boolean" + }, + { + "name": "host", + "description": "Set host address of the dev server, the accessible URL", + "type": "string" + }, + { + "name": "port", + "description": "Set port number of the dev server", + "type": "string" + }, + { + "name": "start", + "description": "Runs the application before starting to perform a testrun", + "type": "boolean" + } + ] } diff --git a/skeleton/protractor/package.json b/skeleton/protractor/package.json index 63cc5da69..bae75cc81 100644 --- a/skeleton/protractor/package.json +++ b/skeleton/protractor/package.json @@ -1,4 +1,8 @@ { + "scripts": { + "e2e": "au protractor", + "e2e:headless": "au protractor --run --start" + }, "devDependencies": { // @if feat.typescript "ts-node": "", @@ -9,4 +13,4 @@ "gulp-protractor": "", "wait-on": "", } -} \ No newline at end of file +} diff --git a/skeleton/protractor/test/protractor.conf.js b/skeleton/protractor/protractor.conf.js similarity index 62% rename from skeleton/protractor/test/protractor.conf.js rename to skeleton/protractor/protractor.conf.js index 6b10ddbb6..d08a71ff3 100644 --- a/skeleton/protractor/test/protractor.conf.js +++ b/skeleton/protractor/protractor.conf.js @@ -1,10 +1,19 @@ -const aureliaConfig = require('../aurelia_project/aurelia.json'); -const port = aureliaConfig.platform.port; +const CLIOptions = require('aurelia-cli').CLIOptions; +const aureliaConfig = require('./aurelia_project/aurelia.json'); +const cliOptions = new CLIOptions(); -exports.config = { - port: port, +Object.assign(cliOptions, { + args: process.argv.slice(3) +}); + +const port = cliOptions.getFlagValue('port') || aureliaConfig.platform.port; +const host = cliOptions.getFlagValue('host') || aureliaConfig.platform.host || "localhost"; +const headless = cliOptions.hasFlag('run') || false; - baseUrl: `http://localhost:${port}/`, +const config = { + port: port, + host: host, + baseUrl: `http://${host}:${port}/`, specs: [ // @if feat.babel @@ -45,11 +54,7 @@ exports.config = { '--disable-translate', '--disable-background-timer-throttling', '--disable-renderer-backgrounding', - '--disable-device-discovery-notifications', - /* enable these if you'd like to test using Chrome Headless - '--no-gpu', - '--headless' - */ + '--disable-device-discovery-notifications' ] } }, @@ -69,3 +74,10 @@ exports.config = { package: 'aurelia-protractor-plugin' }], }; + +if (headless) { + config.capabilities.chromeOptions.args.push("--no-gpu"); + config.capabilities.chromeOptions.args.push("--headless"); +} + +exports.config = config; diff --git a/skeleton/scaffold-minimum/src/main.ext b/skeleton/scaffold-minimum/src/main.ext index 7ce184a19..e2b48ddbb 100644 --- a/skeleton/scaffold-minimum/src/main.ext +++ b/skeleton/scaffold-minimum/src/main.ext @@ -6,8 +6,11 @@ import 'regenerator-runtime/runtime'; // @if feat.typescript import {Aurelia} from 'aurelia-framework' // @endif +// @if ! feat.webpack import environment from './environment'; +// @endif // @if feat.webpack +import * as environment from '../config/environment.json'; import {PLATFORM} from 'aurelia-pal'; // @endif diff --git a/skeleton/scaffold-minimum/test__if_protractor/e2e/demo.e2e.ext b/skeleton/scaffold-minimum/test__if_protractor/e2e/demo.e2e.ext index 1a23020ba..333be4530 100644 --- a/skeleton/scaffold-minimum/test__if_protractor/e2e/demo.e2e.ext +++ b/skeleton/scaffold-minimum/test__if_protractor/e2e/demo.e2e.ext @@ -1,6 +1,6 @@ import {PageObjectWelcome} from './welcome.po'; import {PageObjectSkeleton} from './skeleton.po'; -import {config} from '../protractor.conf'; +import {config} from '../../protractor.conf'; // @if feat.typescript import {browser, element, by, By, $, $$, ExpectedConditions} from 'aurelia-protractor-plugin/protractor'; // @endif @@ -14,7 +14,7 @@ describe('aurelia skeleton app', function() { poSkeleton = new PageObjectSkeleton(); poWelcome = new PageObjectWelcome(); - await browser.loadAndWaitForAureliaPage(`http://localhost:${config.port}`); + await browser.loadAndWaitForAureliaPage(`http://${config.baseUrl}`); }); it('should load the page and display the initial page title', async () => { diff --git a/skeleton/scaffold-navigation/src/main.ext b/skeleton/scaffold-navigation/src/main.ext index 7d27096d7..69b7ccebe 100644 --- a/skeleton/scaffold-navigation/src/main.ext +++ b/skeleton/scaffold-navigation/src/main.ext @@ -7,8 +7,11 @@ import 'bootstrap'; // @if feat.typescript import {Aurelia} from 'aurelia-framework'; // @endif +// @if ! feat.webpack import environment from './environment'; +// @endif // @if feat.webpack +import * as environment from '../config/environment.json'; import {PLATFORM} from 'aurelia-pal'; // @endif diff --git a/skeleton/scaffold-navigation/test__if_protractor/e2e/demo.e2e.ext b/skeleton/scaffold-navigation/test__if_protractor/e2e/demo.e2e.ext index 5bf12b2e9..0a7e4be4b 100644 --- a/skeleton/scaffold-navigation/test__if_protractor/e2e/demo.e2e.ext +++ b/skeleton/scaffold-navigation/test__if_protractor/e2e/demo.e2e.ext @@ -1,6 +1,6 @@ import {PageObjectWelcome} from './welcome.po'; import {PageObjectSkeleton} from './skeleton.po'; -import {config} from '../protractor.conf'; +import {config} from '../../protractor.conf'; // @if feat.typescript import {browser, element, by, By, $, $$, ExpectedConditions} from 'aurelia-protractor-plugin/protractor'; // @endif diff --git a/skeleton/webpack/README.md b/skeleton/webpack/README.md index ba3ea9c6a..30626fc4d 100644 --- a/skeleton/webpack/README.md +++ b/skeleton/webpack/README.md @@ -2,16 +2,20 @@ For more information, go to https://aurelia.io/docs/cli/webpack ## Run dev app -Run `au run`, then open `http://localhost:/* @if feat.web */8080/* @endif *//* @if feat['dotnet-core'] */5000/* @endif */` +Run `npm start`, then open `http://localhost:/* @if feat.web */8080/* @endif *//* @if feat['dotnet-core'] */5000/* @endif */` -To open browser automatically, do `au run --open`. +You can change the standard webpack configurations from CLI easily with something like this: `npm start -- --open --port 8888`. However, it is better to change the respective npm scripts or `webpack.config.js` with these options, as per your need. + +To enable Webpack Bundle Analyzer, do `npm run analyze` (production build). + +To enable hot module reload, do `npm start -- --env.hmr`. To change dev server port, do `au run --port 8888`. -To enable Webpack Bundle Analyzer, do `au run --analyze`. +To change dev server host, do `au run --host 127.0.0.1` -To enable hot module reload, do `au run --hmr`. +**PS:** You could mix all the flags as well, `au run --host 127.0.0.1 --port 7070 --open --env.hmr` ## Build for production -Run `au build --env prod`. +Run `npm run build`. diff --git a/skeleton/webpack/aurelia_project/aurelia.json b/skeleton/webpack/aurelia_project/aurelia.json index 23e789f73..19d570eb9 100644 --- a/skeleton/webpack/aurelia_project/aurelia.json +++ b/skeleton/webpack/aurelia_project/aurelia.json @@ -12,11 +12,13 @@ // @if feat.web "port": 8080, + "host": "localhost", "output": "dist", // @endif // @if feat['dotnet-core'] "port": 5000, + "host": "localhost", "output": "wwwroot/dist", // @endif } diff --git a/skeleton/webpack/aurelia_project/tasks/build.ext b/skeleton/webpack/aurelia_project/tasks/build.ext index e386af35a..1ad979d4b 100644 --- a/skeleton/webpack/aurelia_project/tasks/build.ext +++ b/skeleton/webpack/aurelia_project/tasks/build.ext @@ -1,67 +1,8 @@ -// @if feat.babel -import webpackConfig from '../../webpack.config'; -import webpack from 'webpack'; -import project from '../aurelia.json'; -import gulp from 'gulp'; -import del from 'del'; -// @endif -// @if feat.typescript -import * as webpackConfig from '../../webpack.config'; -import * as webpack from 'webpack'; -import * as project from '../aurelia.json'; -import * as gulp from 'gulp'; -import * as del from 'del'; -// @endif -import {CLIOptions, Configuration} from 'aurelia-cli'; -import configureEnvironment from './environment'; +import { NPM } from 'aurelia-cli'; -const analyze = CLIOptions.hasFlag('analyze'); -const buildOptions = new Configuration(project.build.options); -const production = CLIOptions.getEnvironment() === 'prod'; -const server = buildOptions.isApplicable('server'); -const extractCss = buildOptions.isApplicable('extractCss'); -const coverage = buildOptions.isApplicable('coverage'); +export default function() { + console.log('`au build` is an alias of the `npm run build`, you may use either of those; see README for more details.'); -const config = webpackConfig({ - production, server, extractCss, coverage, analyze -}); -const compiler = webpack(/* @if feat.typescript **/* @endif */config); - -function buildWebpack(done) { - if (CLIOptions.hasFlag('watch')) { - compiler.watch({}, onBuild); - } else { - compiler.run(onBuild); - compiler.hooks.done.tap('done', () => done()); - } -} - -function onBuild(err, stats) { - if (!CLIOptions.hasFlag('watch') && err) { - console.error(err.stack || err); - if (err.details) console.error(err.details); - process.exit(1); - } else { - process.stdout.write(stats.toString({ colors: require('supports-color') }) + '\n'); - - if (!CLIOptions.hasFlag('watch') && stats.hasErrors()) { - process.exit(1); - } - } + const args = process.argv.slice(3); + return (new NPM()).run('run', ['build', '--', ...args]); } - -function clearDist() { - return del([config.output.path]); -} - -const build = gulp.series( - clearDist, - configureEnvironment, - buildWebpack -); - -export { - config, - buildWebpack, - build as default -}; diff --git a/skeleton/webpack/aurelia_project/tasks/build.json b/skeleton/webpack/aurelia_project/tasks/build.json index 27a093605..8cd81dc72 100644 --- a/skeleton/webpack/aurelia_project/tasks/build.json +++ b/skeleton/webpack/aurelia_project/tasks/build.json @@ -1,21 +1,4 @@ -{ - "name": "build", - "description": "Builds and processes all application assets.", - "flags": [ - { - "name": "analyze", - "description": "Enable Webpack Bundle Analyzer. Typically paired with --env prod", - "type": "boolean" - }, - { - "name": "env", - "description": "Sets the build environment.", - "type": "string" - }, - { - "name": "watch", - "description": "Watches source files for changes and refreshes the bundles automatically.", - "type": "boolean" - } - ] +{ + "name": "build", + "description": "Builds and processes all application assets. It is an alias of the `npm run build`, you may use either of those; see README for more details." } diff --git a/skeleton/webpack/aurelia_project/tasks/environment.ext b/skeleton/webpack/aurelia_project/tasks/environment.ext deleted file mode 100644 index 073f46bac..000000000 --- a/skeleton/webpack/aurelia_project/tasks/environment.ext +++ /dev/null @@ -1,36 +0,0 @@ -// @if feat.babel -import project from '../aurelia.json'; -import rename from 'gulp-rename'; -import gulp from 'gulp'; -import fs from 'fs'; -import through from 'through2'; -// @endif -// @if feat.typescript -import * as project from '../aurelia.json'; -import * as rename from 'gulp-rename'; -import * as gulp from 'gulp'; -import * as fs from 'fs'; -import * as through from 'through2'; -// @endif -import {CLIOptions} from 'aurelia-cli'; - -function configureEnvironment() { - let env = CLIOptions.getEnvironment(); - - return gulp.src(`aurelia_project/environments/${env}${project.transpiler.fileExtension}`) - .pipe(rename(`environment${project.transpiler.fileExtension}`)) - .pipe(through.obj(function (file, _, cb) { - // https://github.com/aurelia/cli/issues/1031 - fs.unlink(`${project.paths.root}/${file.relative}`, function () { cb(null, file); }); - })) - .pipe(gulp.dest(project.paths.root)) - .pipe(through.obj(function(file, enc, cb) { - // https://github.com/webpack/watchpack/issues/25#issuecomment-287789288 - const now = Date.now() / 1000; - const then = now - 10; - fs.utimes(file.path, then, then, function(err) { if (err) throw err; }); - cb(null, file); - })); -} - -export default configureEnvironment; diff --git a/skeleton/webpack/aurelia_project/tasks/run.ext b/skeleton/webpack/aurelia_project/tasks/run.ext index 003fea0a1..88f85bfd9 100644 --- a/skeleton/webpack/aurelia_project/tasks/run.ext +++ b/skeleton/webpack/aurelia_project/tasks/run.ext @@ -1,62 +1,41 @@ -// @if feat.babel -import webpack from 'webpack'; -import Server from 'webpack-dev-server'; -import project from '../aurelia.json'; -import gulp from 'gulp'; -// @endif -// @if feat.typescript -import * as webpack from 'webpack'; -import * as Server from 'webpack-dev-server'; +import { NPM } from 'aurelia-cli'; +import { CLIOptions } from 'aurelia-cli'; import * as project from '../aurelia.json'; -import * as gulp from 'gulp'; -// @endif - -import {config} from './build'; -import configureEnvironment from './environment'; -import {CLIOptions, reportWebpackReadiness} from 'aurelia-cli'; - -function runWebpack(done) { - // https://webpack.github.io/docs/webpack-dev-server.html - let opts = { - host: 'localhost', - publicPath: config.output.publicPath, - filename: config.output.filename, - hot: project.platform.hmr || CLIOptions.hasFlag('hmr'), - port: CLIOptions.getFlagValue('port') || project.platform.port, - contentBase: config.output.path, - historyApiFallback: true, - open: project.platform.open || CLIOptions.hasFlag('open'), - stats: { - colors: require('supports-color') - }, - ...config.devServer - }/* @if feat.typescript ** as any/* @endif */; - - // Add the webpack-dev-server client to the webpack entry point - // The path for the client to use such as `webpack-dev-server/client?http://${opts.host}:${opts.port}/` is not required - // The path used is derived from window.location in the browser and output.publicPath in the webpack.config. - if (project.platform.hmr || CLIOptions.hasFlag('hmr')) { - config.plugins.push(new webpack.HotModuleReplacementPlugin()); - config.entry.app.unshift('webpack-dev-server/client', 'webpack/hot/dev-server'); - } else { - // removed "" from index.ejs in favour of this method - config.entry.app.unshift('webpack-dev-server/client'); - } - const compiler = webpack(config); - let server = new Server(compiler, opts); +var find = require('find-process'); +var kill = require('tree-kill'); - server.listen(opts.port, opts.host, function(err) { - if (err) throw err; +const port = CLIOptions.getFlagValue('port') || project.platform.port; +const host = CLIOptions.getFlagValue('host') || project.platform.host || "localhost"; - reportWebpackReadiness(opts); - done(); - }); +const run = () => { + console.log('`au run` is an alias of the `npm start`, you may use either of those; see README for more details.'); + + const args = process.argv.slice(3); + + if (!CLIOptions.hasFlag('port')) { + args.push('--port'); + args.push(port); + } + if (!CLIOptions.hasFlag('host')) { + args.push('--host'); + args.push(host); + } + + return (new NPM()).run('start', ['--', ...args]); } -const run = gulp.series( - configureEnvironment, - runWebpack -); +const shutdownAppServer = () => { + return new Promise(resolve => { + find('port', port) + .then(function (list) { + if (list.length) { + kill(list[0].pid, 'SIGKILL', function (err) { + resolve(); + }); + } + }); + }); +}; -export { run as default }; +export { run as default, shutdownAppServer }; diff --git a/skeleton/webpack/aurelia_project/tasks/run.json b/skeleton/webpack/aurelia_project/tasks/run.json index 99b409d69..fbaad3215 100644 --- a/skeleton/webpack/aurelia_project/tasks/run.json +++ b/skeleton/webpack/aurelia_project/tasks/run.json @@ -1,31 +1,4 @@ -{ - "name": "run", - "description": "Builds the application and serves up the assets via a local web server, watching files for changes as you work.", - "flags": [ - { - "name": "analyze", - "description": "Enable Webpack Bundle Analyzer. Typically paired with --env prod", - "type": "boolean" - }, - { - "name": "env", - "description": "Sets the build environment.", - "type": "string" - }, - { - "name": "hmr", - "description": "Enable Hot Module Reload", - "type": "boolean" - }, - { - "name": "port", - "description": "Set port number of the dev server", - "type": "string" - }, - { - "name": "open", - "description": "Open the default browser at the application location.", - "type": "boolean" - } - ] +{ + "name": "run", + "description": "Builds the application and serves up the assets via a local web server, watching files for changes as you work. It is an alias of the `npm start`, you may use either of those; see README for more details." } diff --git a/skeleton/webpack/package.json b/skeleton/webpack/package.json index 07d3fbe87..9a64c26b1 100644 --- a/skeleton/webpack/package.json +++ b/skeleton/webpack/package.json @@ -49,24 +49,38 @@ "stylus": "", // @endif - "gulp-rename": "", "html-webpack-plugin": "", "copy-webpack-plugin": "", "mini-css-extract-plugin": "", "aurelia-webpack-plugin": "", "duplicate-package-checker-webpack-plugin": "", + "clean-webpack-plugin": "", "webpack": "", "webpack-cli": "", "webpack-dev-server": "", "expose-loader": "", "style-loader": "", "url-loader": "", - "del": "", "css-loader": "", "file-loader": "", + "app-settings-loader": "", "json-loader": "", "html-loader": "", "istanbul-instrumenter-loader": "", "webpack-bundle-analyzer": "", + "find-process": "^1.4.2", + "tree-kill": "^1.2.1", + }, + "scripts": { + "build": "webpack --env.production --env.extractCss", + "build:dev": "webpack --env.extractCss", + "analyze": "webpack --env.production --env.analyze", + // @if feat['dotnet-core'] + "start": "webpack-dev-server --env.extractCss --port 5000" + // @endif + + // @if feat.web + "start": "webpack-dev-server --env.extractCss" + // @endif } -} \ No newline at end of file +} diff --git a/skeleton/webpack/webpack.config.js b/skeleton/webpack/webpack.config.js index 27ac0f908..a0e4030e0 100644 --- a/skeleton/webpack/webpack.config.js +++ b/skeleton/webpack/webpack.config.js @@ -7,6 +7,7 @@ const project = require('./aurelia_project/aurelia.json'); const { AureliaPlugin, ModuleDependenciesPlugin } = require('aurelia-webpack-plugin'); const { ProvidePlugin } = require('webpack'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); +const CleanWebpackPlugin = require('clean-webpack-plugin'); // config helpers: const ensureArray = (config) => config && (Array.isArray(config) ? config : [config]) || []; @@ -17,7 +18,6 @@ const when = (condition, config, negativeConfig) => const title = 'Aurelia Navigation Skeleton'; const outDir = path.resolve(__dirname, project.platform.output); const srcDir = path.resolve(__dirname, 'src'); -const testDir = path.resolve(__dirname, 'test', 'unit'); const nodeModulesDir = path.resolve(__dirname, 'node_modules'); const baseUrl = '/'; @@ -51,7 +51,7 @@ const sassRules = [ ]; // @endif -module.exports = ({ production, server, extractCss, coverage, analyze, karma } = {}) => ({ +module.exports = ({ production, extractCss, analyze, tests, hmr } = {}) => ({ resolve: { // @if feat.typescript extensions: ['.ts', '.js'], @@ -207,7 +207,8 @@ module.exports = ({ production, server, extractCss, coverage, analyze, karma } = devServer: { contentBase: outDir, // serve index.html for all 404 (required for push-state) - historyApiFallback: true + historyApiFallback: true, + hot: hmr }, devtool: production ? 'nosources-source-map' : 'cheap-module-eval-source-map', module: { @@ -279,11 +280,11 @@ module.exports = ({ production, server, extractCss, coverage, analyze, karma } = // @if feat.babel { test: /\.js$/i, loader: 'babel-loader', exclude: nodeModulesDir, - options: coverage ? { sourceMap: 'inline', plugins: ['istanbul'] } : {} + options: tests ? { sourceMap: 'inline', plugins: ['istanbul'] } : {} }, // @endif // @if feat.typescript - { test: /\.ts$/, loader: "ts-loader", options: { reportFiles: [ srcDir+'/**/*.ts'] }, include: karma ? [srcDir, testDir] : srcDir }, + { test: /\.ts$/, loader: "ts-loader" }, // @endif // embed small images and fonts as Data Urls and larger ones as files: { test: /\.(png|gif|jpg|cur)$/i, loader: 'url-loader', options: { limit: 8192 } }, @@ -291,8 +292,11 @@ module.exports = ({ production, server, extractCss, coverage, analyze, karma } = { test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'url-loader', options: { limit: 10000, mimetype: 'application/font-woff' } }, // load these fonts normally, as files: { test: /\.(ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'file-loader' }, + { test: /environment\.json$/i, use: [ + {loader: "app-settings-loader", options: {env: production ? 'production' : 'development' }}, + ]}, // @if feat.typescript - ...when(coverage, { + ...when(tests, { test: /\.[jt]s$/i, loader: 'istanbul-instrumenter-loader', include: srcDir, exclude: [/\.(spec|test)\.[jt]s$/i], enforce: 'post', options: { esModules: true }, @@ -301,7 +305,7 @@ module.exports = ({ production, server, extractCss, coverage, analyze, karma } = ] }, plugins: [ - ...when(!karma, new DuplicatePackageCheckerPlugin()), + ...when(!tests, new DuplicatePackageCheckerPlugin()), new AureliaPlugin(), new ProvidePlugin({ // @if feat['scaffold-navigation'] @@ -337,7 +341,7 @@ module.exports = ({ production, server, extractCss, coverage, analyze, karma } = // @endif metadata: { // available in index.ejs // - title, server, baseUrl + title, baseUrl } }), // ref: https://webpack.js.org/plugins/mini-css-extract-plugin/ @@ -345,8 +349,15 @@ module.exports = ({ production, server, extractCss, coverage, analyze, karma } = filename: production ? 'css/[name].[contenthash].bundle.css' : 'css/[name].[hash].bundle.css', chunkFilename: production ? 'css/[name].[contenthash].chunk.css' : 'css/[name].[hash].chunk.css' })), - ...when(production || server, new CopyWebpackPlugin([ + ...when(!tests, new CopyWebpackPlugin([ { from: 'static', to: outDir, ignore: ['.*'] }])), // ignore dot (hidden) files - ...when(analyze, new BundleAnalyzerPlugin()) + ...when(analyze, new BundleAnalyzerPlugin()), + /** + * Note that the usage of following plugin cleans the webpack output directory before build. + * In case you want to generate any file in the output path as a part of pre-build step, this plugin will likely + * remove those before the webpack build. In that case consider disabling the plugin, and instead use something like + * `del` (https://www.npmjs.com/package/del), or `rimraf` (https://www.npmjs.com/package/rimraf). + */ + new CleanWebpackPlugin() ] });