diff --git a/.vuepress/config.js b/.vuepress/config.js index 1ce0c9c..17dde62 100644 --- a/.vuepress/config.js +++ b/.vuepress/config.js @@ -17,6 +17,7 @@ module.exports = { '/docs/styles.md', '/docs/icons.md', '/docs/utils.md', + '/docs/wordpress.md', '/docs/extending.md', ] } diff --git a/docs/utils.md b/docs/utils.md index bbe0e3e..9133c53 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -8,7 +8,11 @@ To configure them, you can pass these options in your `webpack.config.js`: ```typescript features: { styles: { - browserSync?: boolean; + browserSync?: { + proxy: string; + host?: string; + port?: number; + }; jquery?: 'internal' | 'external'; } } @@ -26,8 +30,9 @@ BrowserSync seems to get around it by overriding paths in every HTML generated by proxied site. In this package, we use `browser-sync-webpack-plugin` to create BrowserSync -instance which proxies our `webpack-dev-server`, which in turn proxies our -WordPress site. Pretty complicated, but works! :tada: +instance which proxies our WordPress site. Pretty complicated, but works! :tada: + +You can read more [here](wordpress.md). ## Difference between internal or external jQuery @@ -40,7 +45,7 @@ done by `external` option here. ::: warning -When using this option, please make sure that jQuery is loaded before webpack in -HTML. +When using this option, please make sure that jQuery is loaded before +webpack-generated assets in HTML. ::: diff --git a/docs/wordpress.md b/docs/wordpress.md new file mode 100644 index 0000000..a2e8578 --- /dev/null +++ b/docs/wordpress.md @@ -0,0 +1,17 @@ +# Using with WordPress + +By default serving JS and CSS from `webpack-dev-server` does not work with +WordPress due to absolute URL paths hardcoded in HTML. However, +[BrowserSync](https://www.browsersync.io/) fixes this problem by replacing paths +in HTML to proxied server. Because of that in WordPress we are using Webpack in +watch mode (`webpack --watch`) with +[`browser-sync-webpack-plugin`](https://github.com/Va1/browser-sync-webpack-plugin) +enabled. + +There are few minor differences between this method and proxying with +`webpack-dev-server`: + +* `webpack`-compiled assets are written to disk instead of memory, +* BrowserSync doesn't reuse webpack-dev-server configuration, [so you have to + configure it in utils section](utils.md), +* JS hot-reloading doesn't work (but CSS works). diff --git a/package.json b/package.json index 77e46c2..338c275 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "webpack": "^4.31.0", "webpack-cli": "^3.3.2", "webpack-dev-server": "^3.1.14", + "webpack-fix-style-only-entries": "^0.2.1", "webpack-merge": "^4.2.1" }, "devDependencies": { diff --git a/src/@types/browser-sync-webpack-plugin.ts b/src/@types/browser-sync-webpack-plugin.ts index 748c9b8..fc9e530 100644 --- a/src/@types/browser-sync-webpack-plugin.ts +++ b/src/@types/browser-sync-webpack-plugin.ts @@ -11,9 +11,9 @@ declare module 'browser-sync-webpack-plugin' { } namespace BrowserSyncPlugin { interface IBrowserSyncOptions { - host: string; - port: number; proxy: string; + host?: string; + port?: number; } interface IBrowserSyncPluginOptions { diff --git a/src/@types/webpack-fix-style-only-entries.ts b/src/@types/webpack-fix-style-only-entries.ts new file mode 100644 index 0000000..3da953e --- /dev/null +++ b/src/@types/webpack-fix-style-only-entries.ts @@ -0,0 +1,19 @@ +/// + +declare module 'webpack-fix-style-only-entries' { + import * as webpack from 'webpack'; + + class WebpackFixStyleOnlyEntries extends webpack.Plugin { + constructor(options?: WebpackFixStyleOnlyEntries.IOptions); + public apply(compiler: webpack.Compiler): void; + } + + namespace WebpackFixStyleOnlyEntries { + interface IOptions { + extensions?: string[]; + silent?: boolean; + } + } + + export = WebpackFixStyleOnlyEntries; +} diff --git a/src/configs/styles.ts b/src/configs/styles.ts index 2502a56..3caaee8 100644 --- a/src/configs/styles.ts +++ b/src/configs/styles.ts @@ -1,5 +1,6 @@ import * as MiniCssExtractPlugin from 'mini-css-extract-plugin'; import * as magicImporter from 'node-sass-magic-importer'; +import * as FixStyleOnlyEntriesPlugin from 'webpack-fix-style-only-entries'; import { IConfiguration, WebpackMode } from '../defaultOptions'; @@ -19,26 +20,26 @@ export function styles({ styles: stylesConfig }: IConfiguration, mode: WebpackMo const fileRegex = stylesConfig.scss ? /\.(sa|sc|c)ss$/ : /\.css$/; const loaders = [ - stylesConfig.extractToFile && mode === 'production' - ? MiniCssExtractPlugin.loader - : 'style-loader', + stylesConfig.extractToFile + ? MiniCssExtractPlugin.loader + : 'style-loader', 'css-loader', 'resolve-url-loader', ]; const plugins = []; + plugins.push(new FixStyleOnlyEntriesPlugin()); + if (stylesConfig.scss) { loaders.push(sassLoader); } if (stylesConfig.extractToFile) { - plugins.push( - new MiniCssExtractPlugin({ - filename: '[name].css', - chunkFilename: '[id].css', - }), - ); + plugins.push(new MiniCssExtractPlugin({ + filename: '[name].css', + chunkFilename: '[id].css', + })); } return { diff --git a/src/configs/utils.ts b/src/configs/utils.ts index dd17604..bf5de78 100644 --- a/src/configs/utils.ts +++ b/src/configs/utils.ts @@ -3,14 +3,14 @@ import { Configuration as WebpackConfiguration, ProvidePlugin } from 'webpack'; import { IConfiguration } from '../defaultOptions'; export interface IUtilOptions { - browserSync?: boolean; + browserSync?: BrowserSyncPlugin.IBrowserSyncOptions; jquery?: 'internal' | 'external'; } function validateConfig(config: IUtilOptions) { if (config.browserSync !== undefined) { - if (typeof config.browserSync !== 'boolean') { - throw new Error('Invalid option for browserSync, boolean should be passed.'); + if (typeof config.browserSync !== 'object') { + throw new Error('Invalid option for browserSync, object should be passed.'); } } @@ -29,18 +29,9 @@ export function utils({ utils: utilsConfig }: IConfiguration) { if (utilsConfig.browserSync) { config.plugins = config.plugins || []; - config.plugins.push( - new BrowserSyncPlugin( - { - host: 'localhost', - port: 3000, - proxy: 'http://localhost:3100/', - }, - { - reload: false, - }, - ), - ); + config.plugins.push(new BrowserSyncPlugin(utilsConfig.browserSync, { + reload: false, + })); } if (utilsConfig.jquery === 'external') { diff --git a/src/configs/webpack.base.ts b/src/configs/webpack.base.ts index 2d13713..6511860 100644 --- a/src/configs/webpack.base.ts +++ b/src/configs/webpack.base.ts @@ -7,7 +7,11 @@ const baseConfig = { use: { loader: 'babel-loader', options: { - presets: [['@babel/preset-env', { modules: false, useBuiltIns: 'usage' }]], + presets: [['@babel/preset-env', { + modules: false, + useBuiltIns: 'usage', + corejs: 2, + }]], }, }, }, diff --git a/src/defaultOptions.ts b/src/defaultOptions.ts index 7969451..aac8e4c 100644 --- a/src/defaultOptions.ts +++ b/src/defaultOptions.ts @@ -22,7 +22,5 @@ export const defaultOptions: IConfiguration = { extractToFile: true, scss: true, }, - utils: { - browserSync: false, - }, + utils: {}, }; diff --git a/test/scss-glob/__snapshots__/test.spec.ts.snap b/test/scss-glob/__snapshots__/test.spec.ts.snap index 22d4e82..f4b4b56 100644 --- a/test/scss-glob/__snapshots__/test.spec.ts.snap +++ b/test/scss-glob/__snapshots__/test.spec.ts.snap @@ -89,51 +89,6 @@ exports[`should import global scss files 1`] = ` /************************************************************************/ /******/ ({ -/***/ \\"./node_modules/css-loader/dist/cjs.js!./node_modules/resolve-url-loader/index.js!./node_modules/sass-loader/lib/loader.js?!./test/scss-glob/src/test.scss\\": -/*!*****************************************************************************************************************************************************************!*\\\\ - !*** ./node_modules/css-loader/dist/cjs.js!./node_modules/resolve-url-loader!./node_modules/sass-loader/lib/loader.js??ref--6-3!./test/scss-glob/src/test.scss ***! - \\\\*****************************************************************************************************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval(\\"exports = module.exports = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ \\\\\\"./node_modules/css-loader/dist/runtime/api.js\\\\\\")(false);\\\\n// Module\\\\nexports.push([module.i, \\\\\\".first {\\\\\\\\n font-weight: bold; }\\\\\\\\n\\\\\\\\n.second {\\\\\\\\n color: #f0f; }\\\\\\\\n\\\\\\", \\\\\\"\\\\\\"]);\\\\n\\\\n\\\\n\\\\n//# sourceURL=webpack:///./test/scss-glob/src/test.scss?./node_modules/css-loader/dist/cjs.js!./node_modules/resolve-url-loader!./node_modules/sass-loader/lib/loader.js??ref--6-3\\"); - -/***/ }), - -/***/ \\"./node_modules/css-loader/dist/runtime/api.js\\": -/*!*****************************************************!*\\\\ - !*** ./node_modules/css-loader/dist/runtime/api.js ***! - \\\\*****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -\\"use strict\\"; -eval(\\"\\\\n\\\\n/*\\\\n MIT License http://www.opensource.org/licenses/mit-license.php\\\\n Author Tobias Koppers @sokra\\\\n*/\\\\n// css base code, injected by the css-loader\\\\nmodule.exports = function (useSourceMap) {\\\\n var list = []; // return the list of modules as css string\\\\n\\\\n list.toString = function toString() {\\\\n return this.map(function (item) {\\\\n var content = cssWithMappingToString(item, useSourceMap);\\\\n\\\\n if (item[2]) {\\\\n return '@media ' + item[2] + '{' + content + '}';\\\\n } else {\\\\n return content;\\\\n }\\\\n }).join('');\\\\n }; // import a list of modules into the list\\\\n\\\\n\\\\n list.i = function (modules, mediaQuery) {\\\\n if (typeof modules === 'string') {\\\\n modules = [[null, modules, '']];\\\\n }\\\\n\\\\n var alreadyImportedModules = {};\\\\n\\\\n for (var i = 0; i < this.length; i++) {\\\\n var id = this[i][0];\\\\n\\\\n if (id != null) {\\\\n alreadyImportedModules[id] = true;\\\\n }\\\\n }\\\\n\\\\n for (i = 0; i < modules.length; i++) {\\\\n var item = modules[i]; // skip already imported module\\\\n // this implementation is not 100% perfect for weird media query combinations\\\\n // when a module is imported multiple times with different media queries.\\\\n // I hope this will never occur (Hey this way we have smaller bundles)\\\\n\\\\n if (item[0] == null || !alreadyImportedModules[item[0]]) {\\\\n if (mediaQuery && !item[2]) {\\\\n item[2] = mediaQuery;\\\\n } else if (mediaQuery) {\\\\n item[2] = '(' + item[2] + ') and (' + mediaQuery + ')';\\\\n }\\\\n\\\\n list.push(item);\\\\n }\\\\n }\\\\n };\\\\n\\\\n return list;\\\\n};\\\\n\\\\nfunction cssWithMappingToString(item, useSourceMap) {\\\\n var content = item[1] || '';\\\\n var cssMapping = item[3];\\\\n\\\\n if (!cssMapping) {\\\\n return content;\\\\n }\\\\n\\\\n if (useSourceMap && typeof btoa === 'function') {\\\\n var sourceMapping = toComment(cssMapping);\\\\n var sourceURLs = cssMapping.sources.map(function (source) {\\\\n return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */';\\\\n });\\\\n return [content].concat(sourceURLs).concat([sourceMapping]).join('\\\\\\\\n');\\\\n }\\\\n\\\\n return [content].join('\\\\\\\\n');\\\\n} // Adapted from convert-source-map (MIT)\\\\n\\\\n\\\\nfunction toComment(sourceMap) {\\\\n // eslint-disable-next-line no-undef\\\\n var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\\\\n var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;\\\\n return '/*# ' + data + ' */';\\\\n}\\\\n\\\\n//# sourceURL=webpack:///./node_modules/css-loader/dist/runtime/api.js?\\"); - -/***/ }), - -/***/ \\"./node_modules/style-loader/lib/addStyles.js\\": -/*!****************************************************!*\\\\ - !*** ./node_modules/style-loader/lib/addStyles.js ***! - \\\\****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval(\\"/*\\\\n\\\\tMIT License http://www.opensource.org/licenses/mit-license.php\\\\n\\\\tAuthor Tobias Koppers @sokra\\\\n*/\\\\n\\\\nvar stylesInDom = {};\\\\n\\\\nvar\\\\tmemoize = function (fn) {\\\\n\\\\tvar memo;\\\\n\\\\n\\\\treturn function () {\\\\n\\\\t\\\\tif (typeof memo === \\\\\\"undefined\\\\\\") memo = fn.apply(this, arguments);\\\\n\\\\t\\\\treturn memo;\\\\n\\\\t};\\\\n};\\\\n\\\\nvar isOldIE = memoize(function () {\\\\n\\\\t// Test for IE <= 9 as proposed by Browserhacks\\\\n\\\\t// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805\\\\n\\\\t// Tests for existence of standard globals is to allow style-loader\\\\n\\\\t// to operate correctly into non-standard environments\\\\n\\\\t// @see https://github.com/webpack-contrib/style-loader/issues/177\\\\n\\\\treturn window && document && document.all && !window.atob;\\\\n});\\\\n\\\\nvar getTarget = function (target, parent) {\\\\n if (parent){\\\\n return parent.querySelector(target);\\\\n }\\\\n return document.querySelector(target);\\\\n};\\\\n\\\\nvar getElement = (function (fn) {\\\\n\\\\tvar memo = {};\\\\n\\\\n\\\\treturn function(target, parent) {\\\\n // If passing function in options, then use it for resolve \\\\\\"head\\\\\\" element.\\\\n // Useful for Shadow Root style i.e\\\\n // {\\\\n // insertInto: function () { return document.querySelector(\\\\\\"#foo\\\\\\").shadowRoot }\\\\n // }\\\\n if (typeof target === 'function') {\\\\n return target();\\\\n }\\\\n if (typeof memo[target] === \\\\\\"undefined\\\\\\") {\\\\n\\\\t\\\\t\\\\tvar styleTarget = getTarget.call(this, target, parent);\\\\n\\\\t\\\\t\\\\t// Special case to return head of iframe instead of iframe itself\\\\n\\\\t\\\\t\\\\tif (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\\\\n\\\\t\\\\t\\\\t\\\\ttry {\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// This will throw an exception if access to iframe is blocked\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// due to cross-origin restrictions\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tstyleTarget = styleTarget.contentDocument.head;\\\\n\\\\t\\\\t\\\\t\\\\t} catch(e) {\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tstyleTarget = null;\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\n\\\\t\\\\t\\\\t}\\\\n\\\\t\\\\t\\\\tmemo[target] = styleTarget;\\\\n\\\\t\\\\t}\\\\n\\\\t\\\\treturn memo[target]\\\\n\\\\t};\\\\n})();\\\\n\\\\nvar singleton = null;\\\\nvar\\\\tsingletonCounter = 0;\\\\nvar\\\\tstylesInsertedAtTop = [];\\\\n\\\\nvar\\\\tfixUrls = __webpack_require__(/*! ./urls */ \\\\\\"./node_modules/style-loader/lib/urls.js\\\\\\");\\\\n\\\\nmodule.exports = function(list, options) {\\\\n\\\\tif (typeof DEBUG !== \\\\\\"undefined\\\\\\" && DEBUG) {\\\\n\\\\t\\\\tif (typeof document !== \\\\\\"object\\\\\\") throw new Error(\\\\\\"The style-loader cannot be used in a non-browser environment\\\\\\");\\\\n\\\\t}\\\\n\\\\n\\\\toptions = options || {};\\\\n\\\\n\\\\toptions.attrs = typeof options.attrs === \\\\\\"object\\\\\\" ? options.attrs : {};\\\\n\\\\n\\\\t// Force single-tag solution on IE6-9, which has a hard limit on the # of