diff --git a/.browser-sync.js b/.browser-sync.js deleted file mode 100644 index 3af2289..0000000 --- a/.browser-sync.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - proxy: 'windycoys.local', - files: [ - 'theme/**/*', - ], - watchTask: true, - open: false, -}; diff --git a/README.md b/README.md index 61d0fc9..0d8100e 100644 --- a/README.md +++ b/README.md @@ -9,55 +9,54 @@ Built using: ## Requirements -* Docker + Docker Compose -* Node.js +* [Docker](https://www.docker.com/products/docker-desktop/) +* [Docker Compose](https://docs.docker.com/compose/install/) +* [Node.js](https://nodejs.org/) -## Building +## Building the theme -The CSS and JavaScript is built with the following command: +The build script runs the following tasks: + +* **css:compile** - Process the CSS +* **js:compile** - Compile the JavaScript to ES5 +* **images** - Minify images + +The CSS and JavaScript will be minified if the environment variable `NODE_ENV='production'`. ``` npm run build ``` -*Note: Files will only be minified if NODE_ENV='production'* - ## Developing -Use `docker-compose` to start the Wordpress and MySQL containers: +The `start` script will monitor CSS, JavaScript and images for changes and automatically compile them. -``` -docker-compose -up -``` - -To build and watch the theme files for changes, run: +It will also start Docker containers and proxy the connection through browser-sync. ``` npm start ``` -*Note: You will either need to set up a host file entry to point -`windycoys.local` to the container's IP or update `.browser-sync.json` to the -container's IP* - -## Linting - ### CSS -CSS is linted with [stylelint](https://stylelint.io) using -[stylelint-selector-bem-pattern](https://github.com/simonsmith/stylelint-selector-bem-pattern) - -``` -npm run css:lint -``` +The CSS is compiled to `theme/style.css` using [postcss-preset-env](https://github.com/csstools/postcss-preset-env) which primarily used to resolve custom properties and add vendor prefixes. It is linted with [stylelint](https://stylelint.io) using [stylelint-selector-bem-pattern](https://github.com/simonsmith/stylelint-selector-bem-pattern). ### JavaScript -JavaScript is linted with [eslint](https://eslint.org/) using -[Airbnb's base config](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb-base) +The JavaScript is transformed to ES5 and bundled to `theme/bundle.js`. It is linted with [eslint](https://eslint.org/) using [Airbnb's base config](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb-base). + +Tests are written with [Jest](https://jestjs.io/) and run with: ``` -npm run js:lint +npm run jest +``` + +The tests are written in `*.spec.js` files along side the source code. + +To watch the files and automatically re-run the tests, use: + +``` +npm run jest:watch ``` ## License diff --git a/docker-compose.yml b/build/docker-compose.yml similarity index 61% rename from docker-compose.yml rename to build/docker-compose.yml index 37dfede..0379216 100644 --- a/docker-compose.yml +++ b/build/docker-compose.yml @@ -3,20 +3,20 @@ wordpress: - WORDPRESS_DB_HOST=db - WORDPRESS_DB_PASSWORD=password - WORDPRESS_DB_USER=windycoys - image: 'wordpress:5.0.0-php5.6-apache' + image: 'wordpress:php7.0-apache' links: - db ports: - "80:80" volumes: - - ./.tmp/wp-content:/var/www/html/wp-content - - ./theme:/var/www/html/wp-content/themes/windycoys + - ../.tmp/wp-content:/var/www/html/wp-content + - ../theme:/var/www/html/wp-content/themes/windycoys db: environment: - MYSQL_DATABASE=wordpress - MYSQL_PASSWORD=password - MYSQL_ROOT_PASSWORD=password - MYSQL_USER=windycoys - image: 'mariadb:10.3.4' + image: 'mariadb:latest' volumes: - - ./.tmp/mysql:/var/lib/mysql + - ../.tmp/mysql:/var/lib/mysql diff --git a/build/serve.js b/build/serve.js new file mode 100644 index 0000000..480711d --- /dev/null +++ b/build/serve.js @@ -0,0 +1,93 @@ +const { resolve } = require('path'); +const { execSync } = require('child_process'); +const { exec, upAll, stop } = require('docker-compose'); +const browsersync = require('browser-sync'); +const mysql = require('mysql'); + +const name = 'windycoys'; + +const dockerOptions = { + cwd: resolve(__dirname), + log: true, +}; + +const browserSyncOptions = { + files: [ + 'theme/**/*', + ], + watchtask: true, + open: false, +}; + +const mysqlConfig = { + user: 'root', + password: 'password', + database: 'wordpress', +}; + +process.env.COMPOSE_PROJECT_NAME = name; + +// Get IP address +const getIpAddr = function getIpAddr(id) { + return execSync(`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${id}`).toString('utf8').trim(); +}; + +// Update URIs in MYSQL database +const updateUriData = function updateUriData(wordpress, db) { + mysqlConfig.host = db; + const con = mysql.createConnection(mysqlConfig); + con.connect(); + + const query = `UPDATE wp_options SET option_value='http://${wordpress}' WHERE option_name in ('siteurl', 'home')`; + + con.query(query, (error) => { + if (error) { + console.error('Error', error.sqlMessage); + process.exit(1); + } + }); + + con.end(); +}; + +// Register Exit handler to stop Docker containers on exit +const exitHandler = function exitHandler() { + stop(dockerOptions).then( + () => { + console.log('\n'); + process.exit(); + }, + (err) => { + console.error('Error:', err.message); + process.exit(1); + }, + ); +}; + +process.on('beforeExit', exitHandler.bind()); +process.on('SIGINT', exitHandler.bind()); +process.on('SIGUSR1', exitHandler.bind()); +process.on('SIGUSR2', exitHandler.bind()); +process.on('uncaughtException', exitHandler.bind()); + +// Start Docker containers +upAll(dockerOptions).then( + () => { + console.log('Docker containers launched, starting browser-sync...'); + const wordpress = getIpAddr(`${name}_wordpress_1`); + const db = getIpAddr(`${name}_db_1`); + if (wordpress && db) { + updateUriData(wordpress, db); + const bs = browsersync.create(); + browserSyncOptions.proxy = wordpress; + bs.init(browserSyncOptions); + } else { + console.error('Error', 'No IP address for Docker containers. Aborting.'); + process.exit(1); + } + }, + (err) => { + console.error('Error', err.message); + process.exit(1); + }, +); diff --git a/package-lock.json b/package-lock.json index a854e34..0efdcd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1406,6 +1406,12 @@ "callsite": "1.0.0" } }, + "bignumber.js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==", + "dev": true + }, "bin-build": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz", @@ -2965,6 +2971,12 @@ "array-find-index": "^1.0.1" } }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -3266,6 +3278,15 @@ } } }, + "docker-compose": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.9.3.tgz", + "integrity": "sha512-8qzzZrIHZJrNu1Y3BHgbIhF2cHQQNqeQKHDWXKYDZyUCcjVspi1z8CN6XOOZWOnn3Cw1gUw1549zlpTLQiUxyQ==", + "dev": true, + "requires": { + "winston": "^2.3.1" + } + }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -4123,6 +4144,12 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", + "dev": true + }, "fast-deep-equal": { "version": "1.1.0", "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", @@ -7721,6 +7748,18 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, + "mysql": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.16.0.tgz", + "integrity": "sha512-dPbN2LHonQp7D5ja5DJXNbCLe/HRdu+f3v61aguzNRQIrmZLOeRoymBYyeThrR6ug+FqzDL95Gc9maqZUJS+Gw==", + "dev": true, + "requires": { + "bignumber.js": "4.1.0", + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + } + }, "nan": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", @@ -12172,6 +12211,12 @@ "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=", + "dev": true + }, "squeak": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz", @@ -12227,6 +12272,12 @@ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, "stack-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", @@ -13626,6 +13677,34 @@ "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=", "dev": true }, + "winston": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz", + "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==", + "dev": true, + "requires": { + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + }, + "dependencies": { + "async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", + "dev": true + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + } + } + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/package.json b/package.json index 3d73016..964e2c1 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "js": "run-s js:*", "js:compile": "rollup --config rollup.config.js", "js:lint": "eslint js/", - "serve": "browser-sync start --config .browser-sync.js", + "serve": "node build/serve.js", "start": "run-p watch serve", "test": "run-s css:lint js:lint jest", "watch": "run-p watch:*", @@ -29,6 +29,8 @@ }, "devDependencies": { "browser-sync": "^2.26.3", + "docker-compose": "^0.9.3", + "mysql": "^2.16.0", "nodemon": "^1.18.7", "pre-commit": "^1.2.2" },