diff --git a/CHANGELOG.md b/CHANGELOG.md index e8b68f4a..25b597a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,13 @@ All notable changes to this project will be documented in this file. Items under Contributors: please follow the recommendations outlined at [keepachangelog.com](http://keepachangelog.com/). Please use the existing headings and styling as a guide, and add a link for the version diff at the bottom of the file. Also, please update the `Unreleased` link to compare to the latest release version. ## [Unreleased] +## [1.1.0] - 2016-08-01 +##### Added +- Support for custom .bootstraprc location. [#114](https://github.com/shakacode/bootstrap-loader/pull/114) by [justin808](https://github.com/justin808) and [pherris](https://github.com/pherris). + ## [1.0.10] - 2016-03-17 ##### Fixed -- fixes polyfill require for node < v4.0.0. [#72](https://github.com/shakacode/bootstrap-loader/pull/72) by [mdgraser](https://github.com/mdgraser). +- Fixes polyfill require for node < v4.0.0. [#72](https://github.com/shakacode/bootstrap-loader/pull/72) by [mdgraser](https://github.com/mdgraser). ## [1.0.9] - 2016-02-28 ##### Fixed @@ -14,7 +18,8 @@ Contributors: please follow the recommendations outlined at [keepachangelog.com] ## [1.0.8] -[Unreleased]: https://github.com/shakacode/bootstrap-loader/compare/1.0.10...master +[Unreleased]: https://github.com/shakacode/bootstrap-loader/compare/1.1.0...master +[1.1.0]: https://github.com/shakacode/bootstrap-loader/compare/1.0.10...1.1.0 [1.0.10]: https://github.com/shakacode/bootstrap-loader/compare/1.0.9...1.0.10 [1.0.9]: https://github.com/shakacode/bootstrap-loader/compare/1.0.8...1.0.9 [1.0.8]: https://github.com/shakacode/bootstrap-loader/compare/1.0.7...1.0.8 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ec781f29..55db71d0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,14 +17,68 @@ To start development simply run: npm start ``` -It will run linters, clear directory with previous build, create new build and run watchers to re-build on every change. Sadly, but we can't use `npm link` feature in development process here because this command symlinks package folder, what makes impossible to resolve `bootstrap` packages in project's `npm_modules` folder. Instead of this just install `bootstrap-loader` locally: +It will run linters, clear directory with previous build, create new build and run watchers to re-build on every change. + + +### Using the Local Library +#### npm link +We can use the `npm link` feature in our development process if we reference full paths to our loader in webpack's config: `bootstrap-loader/lib/bootstrap.loader?extractStyles&configFilePath=${__dirname}/.bootstraprc!bootstrap-loader/no-op.js`. In order for this library to find the expected `bootstrap` version, you must also `npm link` the expected `bootstrap` and `extract-text-webpack-plugin` (assuming you are passing `extractStyles` to `boostrap.loader` e.g. `... + bootstrap.loader?extractStyles&...`) versions from your project's `node_modules` directory to your clone of this library. + +#### Installing locally +More often than not, `npm link` will not work. Maybe you did not set the sibling directories of `bootstrap` and `extract-text-webpack plugin`? + +If `npm link` doesn't work for you, just install `bootstrap-loader` locally: ``` cd my-test-project npm install --save-dev ../path/to/local/bootstrap-loader ``` -It is a pity, but you have to re-install `bootstrap-lodaer` on every change. +Note that if you install `bootstrap-loader` locally, you have to re-install it on every change. Yes. Very inconvenient! + +#### Debug Output +When doing development or debugging, you probably want DEBUG output on. + +Either set an ENV value: `export DEBUG=TRUE` or set debug in the config file. To turn off debugging, etiher `unset DEBUG` or change the debug value in the config file. + +#### Testing changes to the repo +Make sure to write new tests for your changes. Currently the test suite is light, please help us flesh it out. Run the tests with `npm test`. + +You will also want to run the example implementations to ensure they work as expected with your changes. To test: + +Run the package.json scripts in both the `examples/basic` and `examples/css-modules` directories, like this. Be sure to check the browser and look for error messages. + + +``` +cd examples/basic +npm run install-local + +npm run bs3 +# Try out in the browser at localhost:4000, and maybe make changes to the local config .bootstraprc-3-example + +npm run bs4 +# Try out in the browser at localhost:4000, and maybe make changes to the local config .bootstraprc-4-example + +npm run bs3:prod + +npm run bs4:prod + +cd examples/css-modules +npm run install-local + +npm run bs3 +# Try out in the browser at localhost:4000, and maybe make changes to the local config .bootstraprc-3-example + +npm run bs4 +# Try out in the browser at localhost:4000, and maybe make changes to the local config .bootstraprc-4-example + +npm run bs3:prod + +npm run bs4:prod +``` + +Ensure your changes don't break any of the examples before you publish your PR. ### Build To create a build run: @@ -42,7 +96,11 @@ To lint your code run: npm run lint ``` -Shame on us, but we don't have any tests here yet. We will be happy, if you can give us a hand with it. +To test your code run: + +``` +npm run test +``` ## How loader works There are 2 entry points: `./loader.js` & `./extractStyles.js`. These are the dummy loaders, which apply real loader to dummy `no-op.js` file. The source of the real loader is located in `./src/bootstrap.loader.js`. Before exploring things in it, check out `./src/bootstrap.config.js` to figure out how we handle default / user config files & gather options for loader. diff --git a/README.md b/README.md index c6ec7f52..b4503a9a 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ That being said, Bootstrap 4 probably works just fine! ## NEWS +2016-08-01: Released 1.1.0. Supports custom bootstraprc location. 2016-02-28: Released 1.0.9. Updated to support Bootstrap 4, alpha 2! - ## Installation Get it via npm: @@ -35,6 +35,8 @@ npm install bootstrap-sass # or Bootstrap 4 npm install bootstrap@v4.0.0-alpha.2 +# Note, alpha.3 is broken + # Node SASS & other loaders needed to handle styles npm install css-loader node-sass resolve-url-loader sass-loader style-loader url-loader ``` @@ -59,7 +61,11 @@ Or add `bootstrap-loader` as [a module in an entry point](https://webpack.github entry: [ 'bootstrap-loader', './app' ] ``` -Config is optional. It can be placed in root dir with name `.bootstraprc`. You can write it in `YAML` or `JSON` formats. Take a look at the default config files for [Bootstrap 3](.bootstraprc-3-default) and [Bootstrap 4](.bootstraprc-4-default). Note, we recommend using a configuration or else you might pick up unwanted upgrades, such as when we make Bootstrap 4 the default. Config options don't fall back on the defaults once a config file is present. Be sure not to delete config options. To start with a custom config, copy over a default config file as a starting point. +Config is optional. If used, by default it should be placed in your project's root dir with name `.bootstraprc`. You can write it in `YAML` or `JSON` formats. Take a look at the default config files for [Bootstrap 3](.bootstraprc-3-default) and [Bootstrap 4](.bootstraprc-4-default). Note, we recommend using a configuration or else you might pick up unwanted upgrades, such as when we make Bootstrap 4 the default. Config options don't fall back on the defaults once a config file is present. Be sure not to delete config options. To start with a custom config, copy over a default config file as a starting point. + +If the default location doesn't work for you (e.g. you want to create multiple bootstrap configs for branding variations or you symlink your npm_modules directory), you may pass the **absolute path** of the `.bootstraprc` file to the loader in your webpack config, e.g. `bootstrap-loader/lib/bootstrap.loader?extractStyles&configFilePath=${__dirname}/.bootstraprc!bootstrap-loader/no-op.js`. + +Note that :`__dirname` is a [global variable](https://nodejs.org/docs/latest/api/globals.html#globals_dirname) that Node sets for us. It is "the name of the directory that the currently executing script resides in." ```yaml --- @@ -105,6 +111,7 @@ If no config provided, default one for Bootstrap 3 will be used. Check out example apps in [`examples/`](examples) folder: * Basic usage: [examples/basic](examples/basic) + * See the `npm run bs4:customlocation` tasks for examples on how to pass your .bootstraprc config. * With CSS Modules: [examples/css-modules](examples/css-modules) (This example shows off hot reloading with Babel 6 as well!) ## Common Options for Bootstrap 3 and 4 @@ -368,8 +375,6 @@ module: { ``` ## Contributing -This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](CODE_OF_CONDUCT.md). - See [Contributing](CONTRIBUTING.md) to get started. ## License diff --git a/examples/basic/.babelrc b/examples/basic/.babelrc old mode 100644 new mode 100755 diff --git a/examples/basic/.bootstraprc b/examples/basic/.bootstraprc deleted file mode 100644 index 008e6a66..00000000 --- a/examples/basic/.bootstraprc +++ /dev/null @@ -1,125 +0,0 @@ ---- -# Output debugging info -# loglevel: debug - -# Major version of Bootstrap: 3 or 4 -bootstrapVersion: 3 - -# If Bootstrap version 3 is used - turn on/off custom icon font path -useCustomIconFontPath: false - -# Webpack loaders, order matters -styleLoaders: - - style - - css - - sass - -# Extract styles to stand-alone css file -# Different settings for different environments can be used, -# It depends on value of NODE_ENV environment variable -# This param can also be set in webpack config: -# entry: 'bootstrap-loader/extractStyles' -extractStyles: false -# env: -# development: -# extractStyles: false -# production: -# extractStyles: true - -# Customize Bootstrap variables that get imported before the original Bootstrap variables. -# Thus original Bootstrap variables can depend on values from here. All the bootstrap -# variables are configured with !default, and thus, if you define the variable here, then -# that value is used, rather than the default. However, many bootstrap variables are derived -# from other bootstrap variables, and thus, you want to set this up before we load the -# official bootstrap versions. -# For example, _variables.scss contains: -# $input-color: $gray !default; -# This means you can define $input-color before we load _variables.scss -preBootstrapCustomizations: ./app/styles/bootstrap/pre-customizations.scss - -# This gets loaded after bootstrap/variables is loaded and before bootstrap is loaded. -# A good example of this is when you want to override a bootstrap variable to be based -# on the default value of bootstrap. This is pretty specialized case. Thus, you normally -# just override bootrap variables in preBootstrapCustomizations so that derived -# variables will use your definition. -# -# For example, in _variables.scss: -# $input-height: (($font-size-base * $line-height) + ($input-padding-y * 2) + ($border-width * 2)) !default; -# This means that you could define this yourself in preBootstrapCustomizations. Or you can do -# this in bootstrapCustomizations to make the input height 10% bigger than the default calculation. -# Thus you can leverage the default calculations. -# $input-height: $input-height * 1.10; -bootstrapCustomizations: ./app/styles/bootstrap/customizations.scss - -# Import your custom styles here. You have access to all the bootstrap variables. If you require -# your sass files separately, you will not have access to the bootstrap variables, mixins, clases, etc. -# Usually this endpoint-file contains list of @imports of your application styles. -appStyles: ./app/styles/app.scss - -### Bootstrap styles -styles: - - # Mixins - mixins: true - - # Reset and dependencies - normalize: true - print: true - glyphicons: true - - # Core CSS - scaffolding: true - type: true - code: true - grid: true - tables: true - forms: true - buttons: true - - # Components - component-animations: true - dropdowns: true - button-groups: true - input-groups: true - navs: true - navbar: true - breadcrumbs: true - pagination: true - pager: true - labels: true - badges: true - jumbotron: true - thumbnails: true - alerts: true - progress-bars: true - media: true - list-group: true - panels: true - wells: true - responsive-embed: true - close: true - - # Components w/ JavaScript - modals: true - tooltip: true - popovers: true - carousel: true - - # Utility classes - utilities: true - responsive-utilities: true - -### Bootstrap scripts -scripts: - transition: true - alert: true - button: true - carousel: true - collapse: true - dropdown: true - modal: true - tooltip: true - popover: true - scrollspy: true - tab: true - affix: true diff --git a/examples/basic/.bootstraprc-3-example b/examples/basic/.bootstraprc-3-example old mode 100644 new mode 100755 diff --git a/examples/basic/.bootstraprc-4-example b/examples/basic/.bootstraprc-4-example old mode 100644 new mode 100755 diff --git a/examples/basic/.eslintignore b/examples/basic/.eslintignore old mode 100644 new mode 100755 diff --git a/examples/basic/.eslintrc b/examples/basic/.eslintrc old mode 100644 new mode 100755 diff --git a/examples/basic/.gitignore b/examples/basic/.gitignore old mode 100644 new mode 100755 index ec7fcb85..2facf0e8 --- a/examples/basic/.gitignore +++ b/examples/basic/.gitignore @@ -216,3 +216,6 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk + +# the npm run ... commands can create this file but we don't want it checked in +/.bootstraprc diff --git a/examples/basic/README.md b/examples/basic/README.md old mode 100644 new mode 100755 diff --git a/examples/basic/app/markup/bootstrap-dev.html b/examples/basic/app/markup/bootstrap-dev.html old mode 100644 new mode 100755 diff --git a/examples/basic/app/markup/bootstrap-prod.html b/examples/basic/app/markup/bootstrap-prod.html old mode 100644 new mode 100755 diff --git a/examples/basic/app/scripts/app.js b/examples/basic/app/scripts/app.js old mode 100644 new mode 100755 diff --git a/examples/basic/app/styles/app.scss b/examples/basic/app/styles/app.scss old mode 100644 new mode 100755 diff --git a/examples/basic/app/styles/bootstrap/customizations.scss b/examples/basic/app/styles/bootstrap/customizations.scss old mode 100644 new mode 100755 diff --git a/examples/basic/app/styles/bootstrap/pre-customizations.scss b/examples/basic/app/styles/bootstrap/pre-customizations.scss old mode 100644 new mode 100755 diff --git a/examples/basic/app/styles/fonts/OpenSans-Light.ttf b/examples/basic/app/styles/fonts/OpenSans-Light.ttf old mode 100644 new mode 100755 diff --git a/examples/basic/package.json b/examples/basic/package.json index ab0c452a..5a68bb99 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -9,14 +9,22 @@ "scripts": { "start": "nodemon --watch app/markup --ext html server.dev.js", "clean": "rm -rf public/", + "lint": "eslint --ext .js,.jsx .", + "cleanrc": "rm .bootstraprc || echo 'no .bootstraprc file to delete'", "build": "npm run clean && webpack --config webpack.prod.config.js", - "prod": "npm run build && nodemon --watch app/markup --ext html server.prod.js", - "bs3": "rm .bootstraprc && cp .bootstraprc-3-example .bootstraprc", - "bs4": "rm .bootstraprc && cp .bootstraprc-4-example .bootstraprc", - "bs3:dev": "npm run bs3 && npm start", - "bs4:dev": "npm run bs4 && npm start", - "bs3:prod": "npm run bs3 && npm run prod", - "bs4:prod": "npm run bs4 && npm run prod" + "prod": "nodemon --watch app/markup --ext html server.prod.js", + "bs:no-config": "npm run cleanrc && npm start", + "bs3": "npm run cleanrc && nodemon --watch app/markup --ext html server.dev.js --bootstraprc-location=.bootstraprc-3-example", + "bs4": "npm run cleanrc && nodemon --watch app/markup --ext html server.dev.js --bootstraprc-location=.bootstraprc-4-example", + "bs3:prod": "npm run cleanrc && npm run build -- --bootstraprc-location=.bootstraprc-3-example && npm run prod", + "bs4:prod": "npm run cleanrc && npm run build -- --bootstraprc-location=.bootstraprc-4-example && npm run prod", + "bs3:default:setup": "cp -f .bootstraprc-3-example .bootstraprc", + "bs4:default:setup": "cp -f .bootstraprc-4-example .bootstraprc", + "bs3:default:dev": "npm run bs3:default:setup && npm start", + "bs4:default:dev": "npm run bs4:default:setup && npm start", + "bs3:default:prod": "npm run bs3:default:setup && npm run build && npm run prod", + "bs4:default:prod": "npm run bs4:default:setup && npm run build && npm run prod", + "install-local": "npm install --save-dev ../.." }, "repository": { "type": "git", @@ -46,7 +54,7 @@ "babel-loader": "^6.1.0", "babel-preset-es2015": "^6.1.18", "body-parser": "^1.14.1", - "bootstrap-loader": "^1.0.9", + "bootstrap-loader": "*", "css-loader": "^0.22.0", "eslint": "^1.9.0", "eslint-config-airbnb": "^1.0.0", @@ -55,7 +63,7 @@ "font-awesome-loader": "^1.0.0", "imports-loader": "^0.6.5", "node-sass": "^3.4.2", - "nodemon": "^1.8.1", + "nodemon": "^1.9.2", "postcss-loader": "^0.8.1", "resolve-url-loader": "^1.4.3", "sass-loader": "^3.1.1", diff --git a/examples/basic/server.dev.js b/examples/basic/server.dev.js old mode 100644 new mode 100755 diff --git a/examples/basic/server.prod.js b/examples/basic/server.prod.js old mode 100644 new mode 100755 diff --git a/examples/basic/webpack.bootstrap.config.js b/examples/basic/webpack.bootstrap.config.js new file mode 100644 index 00000000..8bf40e5b --- /dev/null +++ b/examples/basic/webpack.bootstrap.config.js @@ -0,0 +1,48 @@ +'use strict'; +const fs = require('fs'); + +function getBootstraprcCustomLocation() { + const matchedArgument = process.argv.find(val => val.includes('--bootstraprc-location')); + return matchedArgument && matchedArgument.split('=')[1]; +} + +const bootstraprcCustomLocation = getBootstraprcCustomLocation(); + +let defaultBootstraprcFileExists; + +try { + fs.statSync('./.bootstraprc'); + defaultBootstraprcFileExists = true; +} catch (e) { + defaultBootstraprcFileExists = false; +} + +if (!bootstraprcCustomLocation && !defaultBootstraprcFileExists) { + /* eslint no-console: 0 */ + console.log('You did not specify a \'bootstraprc-location\' arg or a ./.boostraprc file in the root.'); + console.log('Using the bootstrap-loader default configuration.'); +} + +// DEV and PROD have slightly different configurations +let bootstrapDevEntryPoint; +if (bootstraprcCustomLocation) { + bootstrapDevEntryPoint = 'bootstrap-loader/lib/bootstrap.loader?' + + `configFilePath=${__dirname}/${bootstraprcCustomLocation}` + + '!bootstrap-loader/no-op.js'; +} else { + bootstrapDevEntryPoint = 'bootstrap-loader'; +} + +let bootstrapProdEntryPoint; +if (bootstraprcCustomLocation) { + bootstrapProdEntryPoint = 'bootstrap-loader/lib/bootstrap.loader?extractStyles' + + `&configFilePath=${__dirname}/${bootstraprcCustomLocation}` + + '!bootstrap-loader/no-op.js'; +} else { + bootstrapProdEntryPoint = 'bootstrap-loader/extractStyles'; +} + +module.exports = { + dev: bootstrapDevEntryPoint, + prod: bootstrapProdEntryPoint, +}; diff --git a/examples/basic/webpack.dev.config.js b/examples/basic/webpack.dev.config.js old mode 100644 new mode 100755 index 73fa627a..07c7b5e1 --- a/examples/basic/webpack.dev.config.js +++ b/examples/basic/webpack.dev.config.js @@ -4,6 +4,10 @@ const webpack = require('webpack'); const path = require('path'); const autoprefixer = require('autoprefixer'); +const bootstrapEntryPoints = require('./webpack.bootstrap.config.js'); + +// eslint-disable-next-line no-console +console.log(`=> bootstrap-loader configuration: ${bootstrapEntryPoints.dev}`); module.exports = { @@ -11,7 +15,7 @@ module.exports = { 'webpack-hot-middleware/client', 'tether', 'font-awesome-loader', - 'bootstrap-loader', + bootstrapEntryPoints.dev, './app/scripts/app', ], diff --git a/examples/basic/webpack.prod.config.js b/examples/basic/webpack.prod.config.js old mode 100644 new mode 100755 index 486ba0f3..6feeda83 --- a/examples/basic/webpack.prod.config.js +++ b/examples/basic/webpack.prod.config.js @@ -5,6 +5,10 @@ const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const path = require('path'); const autoprefixer = require('autoprefixer'); +const bootstrapEntryPoints = require('./webpack.bootstrap.config.js'); + +// eslint-disable-next-line no-console +console.log(`=> bootstrap-loader configuration: ${bootstrapEntryPoints.prod}`); module.exports = { @@ -12,7 +16,7 @@ module.exports = { // Provide `extractStyles` param and `bootstrap-loader` will handle it entry: [ 'font-awesome-loader', - 'bootstrap-loader/extractStyles', + bootstrapEntryPoints.prod, 'tether', './app/scripts/app', ], diff --git a/examples/css-modules/.bootstraprc b/examples/css-modules/.bootstraprc index 9f6039f5..829123d4 100644 --- a/examples/css-modules/.bootstraprc +++ b/examples/css-modules/.bootstraprc @@ -3,15 +3,16 @@ # loglevel: debug # Major version of Bootstrap: 3 or 4 -bootstrapVersion: 3 +bootstrapVersion: 4 -# If Bootstrap version 3 is used - turn on/off custom icon font path -useCustomIconFontPath: false +# If Bootstrap version 4 is used - turn on/off flexbox model +useFlexbox: true # Webpack loaders, order matters styleLoaders: - style - css + - postcss - sass # Extract styles to stand-alone css file @@ -46,11 +47,11 @@ styles: # Reset and dependencies normalize: true print: true - glyphicons: true # Core CSS - scaffolding: true + reboot: true type: true + images: true code: true grid: true tables: true @@ -58,37 +59,36 @@ styles: buttons: true # Components - component-animations: true - dropdowns: true - button-groups: true - input-groups: true - navs: true + animation: true + dropdown: true + button-group: true + input-group: true + custom-forms: true + nav: true navbar: true - breadcrumbs: true + card: true + breadcrumb: true pagination: true pager: true labels: true - badges: true jumbotron: true - thumbnails: true - alerts: true - progress-bars: true + alert: true + progress: true media: true list-group: true - panels: true - wells: true responsive-embed: true close: true # Components w/ JavaScript - modals: true + modal: true tooltip: true - popovers: true + popover: true carousel: true # Utility classes utilities: true - responsive-utilities: true + utilities-spacing: true + utilities-responsive: true ### Bootstrap scripts scripts: false diff --git a/examples/css-modules/package.json b/examples/css-modules/package.json index b6be3e63..f5274da6 100644 --- a/examples/css-modules/package.json +++ b/examples/css-modules/package.json @@ -9,14 +9,21 @@ "start": "nodemon --watch server.dev.js server.dev.js", "lint": "eslint --ext .js,.jsx .", "clean": "rm -rf public/", + "cleanrc": "rm .bootstraprc || echo 'no .bootstraprc file to delete'", "build": "npm run clean && webpack --config webpack.prod.config.js", "prod": "NODE_ENV=production npm run build && nodemon --watch server.prod.js server.prod.js", - "bs3": "rm .bootstraprc && cp .bootstraprc-3-example .bootstraprc", - "bs4": "rm .bootstraprc && cp .bootstraprc-4-example .bootstraprc", - "bs3:dev": "npm run bs3 && npm start", - "bs4:dev": "npm run bs4 && npm start", - "bs3:prod": "npm run bs3 && npm run prod", - "bs4:prod": "npm run bs4 && npm run prod" + "bs:no-config": "npm run cleanrc && npm start", + "bs3": "npm run cleanrc && nodemon --watch app/markup --ext html server.dev.js --bootstraprc-location=.bootstraprc-3-example", + "bs4": "npm run cleanrc && nodemon --watch app/markup --ext html server.dev.js --bootstraprc-location=.bootstraprc-4-example", + "bs3:prod": "npm run cleanrc && npm run build -- --bootstraprc-location=.bootstraprc-3-example && npm run prod", + "bs4:prod": "npm run cleanrc && npm run build -- --bootstraprc-location=.bootstraprc-4-example && npm run prod", + "bs3:default-location:setup": "cp -f .bootstraprc-3-example .bootstraprc", + "bs4:default-location:setup": "cp -f .bootstraprc-4-example .bootstraprc", + "bs3:default-location:dev": "npm run bs3:default-location:setup && npm start", + "bs4:default-location:dev": "npm run bs4:default-location:setup && npm start", + "bs3:default-location:prod": "npm run bs3:default-location:setup && npm run prod", + "bs4:default-location:prod": "npm run bs4:default-location:setup && npm run prod", + "install-local": "npm install --save-dev ../.." }, "authors": [ "Justin Gordon (https://github.com/justin808)", @@ -54,7 +61,7 @@ "babel-plugin-react-transform": "^2.0.0-beta1", "babel-preset-es2015": "^6.3.13", "babel-preset-react": "^6.3.13", - "bootstrap-loader": "^1.0.9", + "bootstrap-loader": "*", "css-loader": "^0.23.0", "eslint": "^1.9.0", "eslint-config-airbnb": "^1.0.0", diff --git a/examples/css-modules/webpack.bootstrap.config.js b/examples/css-modules/webpack.bootstrap.config.js new file mode 100644 index 00000000..8bf40e5b --- /dev/null +++ b/examples/css-modules/webpack.bootstrap.config.js @@ -0,0 +1,48 @@ +'use strict'; +const fs = require('fs'); + +function getBootstraprcCustomLocation() { + const matchedArgument = process.argv.find(val => val.includes('--bootstraprc-location')); + return matchedArgument && matchedArgument.split('=')[1]; +} + +const bootstraprcCustomLocation = getBootstraprcCustomLocation(); + +let defaultBootstraprcFileExists; + +try { + fs.statSync('./.bootstraprc'); + defaultBootstraprcFileExists = true; +} catch (e) { + defaultBootstraprcFileExists = false; +} + +if (!bootstraprcCustomLocation && !defaultBootstraprcFileExists) { + /* eslint no-console: 0 */ + console.log('You did not specify a \'bootstraprc-location\' arg or a ./.boostraprc file in the root.'); + console.log('Using the bootstrap-loader default configuration.'); +} + +// DEV and PROD have slightly different configurations +let bootstrapDevEntryPoint; +if (bootstraprcCustomLocation) { + bootstrapDevEntryPoint = 'bootstrap-loader/lib/bootstrap.loader?' + + `configFilePath=${__dirname}/${bootstraprcCustomLocation}` + + '!bootstrap-loader/no-op.js'; +} else { + bootstrapDevEntryPoint = 'bootstrap-loader'; +} + +let bootstrapProdEntryPoint; +if (bootstraprcCustomLocation) { + bootstrapProdEntryPoint = 'bootstrap-loader/lib/bootstrap.loader?extractStyles' + + `&configFilePath=${__dirname}/${bootstraprcCustomLocation}` + + '!bootstrap-loader/no-op.js'; +} else { + bootstrapProdEntryPoint = 'bootstrap-loader/extractStyles'; +} + +module.exports = { + dev: bootstrapDevEntryPoint, + prod: bootstrapProdEntryPoint, +}; diff --git a/examples/css-modules/webpack.dev.config.js b/examples/css-modules/webpack.dev.config.js index b6e3bdcc..84796c0f 100644 --- a/examples/css-modules/webpack.dev.config.js +++ b/examples/css-modules/webpack.dev.config.js @@ -4,6 +4,10 @@ const webpack = require('webpack'); const path = require('path'); const autoprefixer = require('autoprefixer'); +const bootstrapEntryPoints = require('./webpack.bootstrap.config.js'); + +// eslint-disable-next-line no-console +console.log(`=> bootstrap-loader configuration: ${bootstrapEntryPoints.dev}`); module.exports = { @@ -11,7 +15,7 @@ module.exports = { 'webpack-hot-middleware/client', 'tether', 'font-awesome-loader', - 'bootstrap-loader', + bootstrapEntryPoints.dev, './app/startup/App', ], diff --git a/examples/css-modules/webpack.prod.config.js b/examples/css-modules/webpack.prod.config.js index 619d3be4..448ab040 100644 --- a/examples/css-modules/webpack.prod.config.js +++ b/examples/css-modules/webpack.prod.config.js @@ -5,6 +5,10 @@ const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const path = require('path'); const autoprefixer = require('autoprefixer'); +const bootstrapEntryPoints = require('./webpack.bootstrap.config.js'); + +// eslint-disable-next-line no-console +console.log(`=> bootstrap-loader configuration: ${bootstrapEntryPoints.prod}`); module.exports = { @@ -12,7 +16,7 @@ module.exports = { // Provide `extractStyles` param and `bootstrap-loader` will handle it entry: [ 'font-awesome-loader', - 'bootstrap-loader/extractStyles', + bootstrapEntryPoints.prod, 'tether', './app/startup/App', ], diff --git a/node_package/scripts/test b/node_package/scripts/test new file mode 100755 index 00000000..68acf587 --- /dev/null +++ b/node_package/scripts/test @@ -0,0 +1,10 @@ +#!/bin/sh + +echo "Running JS tests in node" +$(npm bin)/babel-tape-runner node_package/tests/*.js | $(npm bin)/tap-spec +# $(npm bin)/babel-tape-runner node_package/tests/*.js | $(npm bin)/faucet + +# If we want to run tests in the browser +# echo "Running JS tests in browser" +# $(npm bin)/browserify -t babelify node_package/tests/*.js | tape-run | $(npm bin)/faucet + diff --git a/node_package/tests/configCreation.test.js b/node_package/tests/configCreation.test.js new file mode 100644 index 00000000..6a8c97ab --- /dev/null +++ b/node_package/tests/configCreation.test.js @@ -0,0 +1,10 @@ +import test from 'tape'; +import { userConfigFileExists } from '../../src/bootstrap.config.js'; + +test('userConfigFileExists returns true for files that exist', (assert) => { + const result = userConfigFileExists(`${__dirname}/configCreation.test.js`); + + assert.plan(1); + assert.equal(result, true); + assert.end(); +}); diff --git a/package.json b/package.json index 27fd4620..567f2e36 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { "name": "bootstrap-loader", - "version": "1.0.10", + "version": "1.1.0-beta.1", "description": "Boostrap for Webpack", "main": "loader.js", "scripts": { + "test": "node_package/scripts/test", "start": "npm run lint && npm run clean && npm run dev", "dev": "babel --watch --out-dir lib src", "build": "babel --out-dir lib src", @@ -55,7 +56,10 @@ "babel": "^6.0.15", "babel-cli": "^6.1.4", "babel-preset-es2015": "^6.1.4", + "babel-tape-runner": "^2.0.1", "eslint": "^1.10.3", - "eslint-config-airbnb": "^2.0.0" + "eslint-config-shakacode": "^5.0.0", + "tap-spec": "^4.1.1", + "tape": "^4.5.1" } } diff --git a/src/bootstrap.config.js b/src/bootstrap.config.js index 3be9a1bc..f0d35842 100644 --- a/src/bootstrap.config.js +++ b/src/bootstrap.config.js @@ -6,64 +6,79 @@ import parseConfig from './utils/parseConfig'; import selectModules from './utils/selectModules'; import selectUserModules from './utils/selectUserModules'; import getEnvProp from './utils/getEnvProp'; +import logger from './utils/logger'; /* ======= Fetching config */ const DEFAULT_VERSION = 3; const SUPPORTED_VERSIONS = [3, 4]; const CONFIG_FILE = '.bootstraprc'; - -const userConfigPath = path.resolve(__dirname, `../../../${CONFIG_FILE}`); -const isUserConfig = fileExists(userConfigPath); +const defaultUserConfigPath = `../../../${CONFIG_FILE}`; let rawConfig; let defaultConfig; -if (isUserConfig) { - rawConfig = parseConfig(userConfigPath); - - const { bootstrapVersion } = rawConfig; +function userConfigFileExists(userConfigPath) { + return userConfigPath && fileExists(userConfigPath); +} - if (!bootstrapVersion) { - throw new Error(` - I can't find Bootstrap version in your '.bootstraprc'. - Make sure it's set to 3 or 4. Like this: - bootstrapVersion: 4 - `); - } +function setConfigVariables(configFilePath) { + if (configFilePath) { + rawConfig = parseConfig(configFilePath); + + if (!rawConfig) { + throw new Error(`No config file at ${configFilePath}'`); + } + + const { bootstrapVersion } = rawConfig; + + if (!bootstrapVersion) { + throw new Error(` + I can't find Bootstrap version in your '.bootstraprc'. + Make sure it's set to 3 or 4. Like this: + bootstrapVersion: 4 + `); + } + + if (SUPPORTED_VERSIONS.indexOf(parseInt(bootstrapVersion, 10)) === -1) { + throw new Error(` + Looks like you have unsupported Bootstrap version in your '.bootstraprc'. + Make sure it's set to 3 or 4. Like this: + bootstrapVersion: 4 + `); + } + + const defaultConfigPath = ( + resolveDefaultConfigPath(CONFIG_FILE, bootstrapVersion) + ); + defaultConfig = parseConfig(defaultConfigPath); + } else { + const defaultConfigPath = ( + resolveDefaultConfigPath(CONFIG_FILE, DEFAULT_VERSION) + ); + rawConfig = defaultConfig = parseConfig(defaultConfigPath); - if (SUPPORTED_VERSIONS.indexOf(parseInt(bootstrapVersion, 10)) === -1) { - throw new Error(` - Looks like you have unsupported Bootstrap version in your '.bootstraprc'. - Make sure it's set to 3 or 4. Like this: - bootstrapVersion: 4 - `); + if (!rawConfig) { + throw new Error(`No default config file at ${defaultConfigPath}'`); + } } - - const defaultConfigPath = ( - resolveDefaultConfigPath(CONFIG_FILE, bootstrapVersion) - ); - defaultConfig = parseConfig(defaultConfigPath); -} else { - const defaultConfigPath = ( - resolveDefaultConfigPath(CONFIG_FILE, DEFAULT_VERSION) - ); - rawConfig = defaultConfig = parseConfig(defaultConfigPath); } /* ======= Exports */ - -export const bootstrapVersion = parseInt(rawConfig.bootstrapVersion, 10); -export const loglevel = rawConfig.loglevel; +export { userConfigFileExists }; export function createConfig({ - bootstrapPath, - bootstrapRelPath, extractStyles, + configFilePath = defaultUserConfigPath, }) { - if (isUserConfig) { - const configDir = path.dirname(userConfigPath); + const configFile = path.resolve(__dirname, configFilePath); + + if (userConfigFileExists(configFile)) { + logger.log(`bootstrap-loader is using config file at ${configFile}`); + + setConfigVariables(configFile); + const configDir = path.dirname(configFile); const preBootstrapCustomizations = ( rawConfig.preBootstrapCustomizations && path.resolve(configDir, rawConfig.preBootstrapCustomizations) @@ -78,10 +93,8 @@ export function createConfig({ ); return { - bootstrapPath, - bootstrapRelPath, - bootstrapVersion, - loglevel, + bootstrapVersion: parseInt(rawConfig.bootstrapVersion, 10), + loglevel: rawConfig.loglevel, preBootstrapCustomizations, bootstrapCustomizations, appStyles, @@ -94,11 +107,16 @@ export function createConfig({ }; } + if (configFile) { + logger.log(`bootstrap-loader config file ${configFile} was not found. + Using default bootstrap 3 configuration`); + } + + // Or else we're using the default config file + setConfigVariables(); return { - bootstrapPath, - bootstrapRelPath, - bootstrapVersion, - loglevel, + bootstrapVersion: parseInt(rawConfig.bootstrapVersion, 10), + loglevel: rawConfig.loglevel, useFlexbox: defaultConfig.useFlexbox, useCustomIconFontPath: defaultConfig.useCustomIconFontPath, extractStyles: extractStyles || getEnvProp('extractStyles', defaultConfig), diff --git a/src/bootstrap.loader.js b/src/bootstrap.loader.js index 8b3be6f0..45426265 100644 --- a/src/bootstrap.loader.js +++ b/src/bootstrap.loader.js @@ -33,7 +33,7 @@ import joinLoaders from './utils/joinLoaders'; import buildExtractStylesLoader from './utils/buildExtractStylesLoader'; import createRequire from './utils/createRequire'; import logger from './utils/logger'; -import { bootstrapVersion, loglevel, createConfig } from './bootstrap.config'; +import { createConfig, userConfigFileExists } from './bootstrap.config'; module.exports = function() {}; @@ -47,35 +47,67 @@ module.exports = function() {}; module.exports.pitch = function(source) { if (this.cacheable) this.cacheable(); - global.__DEBUG__ = loglevel === 'debug' || process.env.DEBUG === '*'; + const { extractStyles, configFilePath } = loaderUtils.parseQuery(this.query); - logger.debug(`Hey, we're in DEBUG mode! Yabba dabba doo!`); + if (configFilePath) { + const fullPathToUserConfig = path.resolve(__dirname, configFilePath); + if (!userConfigFileExists(fullPathToUserConfig)) { + throw new Error(` + Cannot find config file ${fullPathToUserConfig}. You might want to pass the full path. + `); + } + } - logger.debug('Context:', this.context); - logger.debug('Using Bootstrap version:', bootstrapVersion); + const config = ( + createConfig({ extractStyles, configFilePath }) + ); + + const loglevel = config.loglevel; + + global.__DEBUG__ = loglevel === 'debug' || process.env.DEBUG; + + if (global.__DEBUG__) { + logger.debug(`Hey, we're in DEBUG mode because you have ` + + (process.env.DEBUG + ? 'DEBUG defined in your ENV.' + : 'your config log level set to \'debug\'.')); + + logger.debug('Query from webpack config:', this.query || '*none*'); + } + + const bootstrapVersion = config.bootstrapVersion; // Resolve `bootstrap` package const bootstrapNPMModule = ( bootstrapVersion === 3 ? 'bootstrap-sass' : 'bootstrap' ); + logger.debug('Using Bootstrap module:', bootstrapNPMModule); - const bootstrapPath = resolveModule(bootstrapNPMModule); + config.bootstrapPath = resolveModule(bootstrapNPMModule); + logger.debug(`Bootstrap module location (abs): ${config.bootstrapPath}`); + if (!config.bootstrapPath) { + throw new Error(` +Could not find bootstrap version: '${bootstrapVersion}'. Did you install it? +The package is 'bootstrap' for bootstrap v4 and 'bootstrap-sass' for v3. +`); + } + + config.bootstrapRelPath = path.relative(this.context, config.bootstrapPath); + logger.debug(`Bootstrap module location (rel): ${config.bootstrapRelPath}`); + + logger.debug('Context:', this.context); + logger.debug('Using Bootstrap version:', bootstrapVersion); - if (!bootstrapPath) { + if (!config.bootstrapPath) { throw new Error(` Could not find path to '${bootstrapNPMModule}' module. Make sure it's installed in your 'node_modules/' directory. `); } - const bootstrapRelPath = path.relative(this.context, bootstrapPath); - - logger.debug(`Bootstrap module location (abs):`, bootstrapPath); - logger.debug(`Bootstrap module location (rel):`, bootstrapRelPath); - const bootstrapNPMVersion = ( - checkBootstrapVersion(bootstrapVersion, bootstrapPath) + checkBootstrapVersion(bootstrapVersion, config.bootstrapPath) ); if (!bootstrapNPMVersion.allGood) { @@ -85,14 +117,9 @@ module.exports.pitch = function(source) { Installed version: ${bootstrapNPMVersion.version} `); } - logger.debug('Bootstrap NPM package version:', bootstrapNPMVersion.version); - const { extractStyles } = loaderUtils.parseQuery(this.query); - logger.debug('Query from webpack config:', this.query || '*none*'); + logger.debug('Bootstrap NPM package version:', bootstrapNPMVersion.version); - const config = ( - createConfig({ bootstrapPath, bootstrapRelPath, extractStyles }) - ); logger.debug('Normalized params:', '\n', config); global.__BOOTSTRAP_CONFIG__ = config; diff --git a/src/utils/resolveModule.js b/src/utils/resolveModule.js index e6c8e33f..b7ab76e2 100644 --- a/src/utils/resolveModule.js +++ b/src/utils/resolveModule.js @@ -1,5 +1,5 @@ import resolve from 'resolve'; - +import logger from './logger'; /** * Resolves location of npm module * @@ -17,6 +17,7 @@ export default function(module) { }); return resolvedPath; } catch (error) { + logger.debug('resolveModule error is ', error); return false; } }