[
  {
    "path": ".gitignore",
    "content": "# general\n.DS_Store\nnode_modules\ndist\n\n# prefer npm\nyarn.lock\n\n# exclude testing output\noutput"
  },
  {
    "path": ".npmignore",
    "content": "# gitignore\n.DS_Store\nnode_modules\ndist\nyarn.lock\noutput\n\n# exclude for npm only\nexamples\n.travis.yml\nREADME.md"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"lts/*\"\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Media Query Plugin\n\n[![Npm Version](https://badge.fury.io/js/media-query-plugin.svg)](https://www.npmjs.com/package/media-query-plugin) \n[![Build Status](https://travis-ci.com/SassNinja/media-query-plugin.svg?branch=master)](https://travis-ci.com/SassNinja/media-query-plugin) \n[![Month Downloads](https://img.shields.io/npm/dm/media-query-plugin.svg)](http://npm-stat.com/charts.html?package=media-query-plugin)\n\nHave you ever thought about extracting your media queries from your CSS so a mobile user doesn't have to load desktop specific CSS?\nIf so this plugin is what you need!\n\nWhen writing CSS with the help of a framework (such as [Bootstrap](https://getbootstrap.com/) or [Foundation](https://get.foundation/sites.html)) and with a modular design pattern you'll mostly end up with CSS that contains all media queries. Using this plugin lets you easily extract the media queries from your CSS and load it async.\n\nSo instead of forcing the user to load this\n\n```css\n.foo { color: red }\n@media print, screen and (min-width: 75em) {\n    .foo { color: blue }\n}\n.bar { font-size: 1rem }\n```\n\nhe only has to load this always\n\n```css\n.foo { color: red }\n.bar { font-size: 1rem }\n```\n\nand on desktop viewport size this in addition\n\n```css\n@media print, screen and (min-width: 75em) {\n    .foo { color: blue }\n}\n```\n\n\n## Prerequisites\n\nYou should already have a working webpack configuration before you try to use this plugin. If you haven't used webpack yet please go through the [webpack guide](https://webpack.js.org/guides/) first and start using this awesome tool for your assets mangement!\n\n## Installation\n\nSimply install the package with your prefered package manager.\n\n- npm\n```bash\nnpm install media-query-plugin --save-dev\n```\n\n- yarn\n```bash\nyarn add media-query-plugin --dev\n```\n\n## Let's get started\n\n### 1. Loader\n\nThe plugin comes together with a loader which takes care of the CSS extraction from the source and provides it for the injection afterwards.\n\n**Important:** make sure the loader receives plain CSS so place it between the css-loader and the sass-loader/less-loader.\n\n```javascript\nconst MediaQueryPlugin = require('media-query-plugin');\n\nmodule.exports = {\n    module: {\n        rules: [\n            {\n                test: /\\.scss$/,\n                use: [\n                    MiniCssExtractPlugin.loader,\n                    'css-loader',\n                    MediaQueryPlugin.loader,\n                    'postcss-loader',\n                    'sass-loader'\n                ]\n            }\n        ]\n    }\n};\n```\n\n### 2. Plugin\n\nAdd the plugin to your webpack config. It will inject the extracted CSS of the loader after the compilation. To identify the target file for the injection it'll look for `[name]-[query]`. So if CSS with the query `desktop` is extracted from `example.scss`, it'll look for `example-desktop` to do the injection. In case there's no match the extracted CSS gets simply emited as CSS file (it doesn't disappear in nirvana :wink:).\n\n```javascript\nconst MediaQueryPlugin = require('./plugins/media-query-plugin');\n\nmodule.exports = {\n    plugins: [\n        new MediaQueryPlugin({\n            include: [\n                'example'\n            ],\n            queries: {\n                'print, screen and (min-width: 75em)': 'desktop'\n            }\n        })\n    ]\n};\n```\n\n### 3. Use Extracted Files\n\nIf you import the extracted CSS (mostly as dynamic import with viewport condition), webpack will try to resolve that import and throw an error if the file does not exist. Thus you have to create those files manually before running webpack. Empty files as placeholder do the job (the get filled later by the plugin).\n\n**Important:** as mentioned above the name of those files must follow the pattern `[name]-[query]` so an example file could be `example-desktop.scss`\n\n```javascript\nimport './example.scss';\n\nif (window.innerWidth >= 960) {\n    import(/* webpackChunkName: 'example-desktop' */ './example-desktop.scss');\n}\n```\n\n## Options\n\nThe following options are available.\n\n| name        | mandatory |\n| ----------- | --------- |\n| include     | yes       |\n| queries     | yes       |\n| groups      | no        |\n\n### include\n\nEach chunk (which uses the loader) gets checked if its name matches this option. In case of a match each query specified in the `queries` options gets extracted from the chunk.\n\nPossible types\n- array (e.g. `['example']`)\n- regex (e.g. `/example/`)\n- boolean (e.g. `true`)\n\n### queries\n\nThis option tells the plugin which media queries are supposed to get extracted. If a media query doesn't match it'll stay untouched. Otherwise it gets extracted and afterwards injected.\n\n**Important:** make sure the queries match 100% the source CSS rule excl the `@media`.\n\n**Tip:** you can use the same name for different media queries to concatenate them (e.g. desktop portrait and desktop landscape)\n\n```javascript\nqueries: {\n    'print, screen and (max-width: 60em) and (orientation: portrait)': 'desktop',\n    'print, screen and (max-width: 60em) and (orientation: landscape)': 'desktop'\n}\n```\n\n### groups\n\nBy default the name of the extracted CSS file(s) is `[chunk]-[query]`. This option lets you map chunk names to a specific group name what results in `[group]-[query]`.\nSo the following code would generate a `app-desktop.css` instead of `exampleA-desktop.css` and `exampleB-desktop.css`. This can be useful when working with [splitChunks](https://webpack.js.org/plugins/split-chunks-plugin/).\n\n```javascript\ngroups: {\n    app: ['exampleA', 'exampleB']\n}\n```\n\n**Tip:** you can also use regex to target chunks\n```javascript\ngroups: {\n    app: /^example/\n}\n```\n\n## Other Webpack Plugins\n\nThis plugin plays together well with the following other webpack plugins.\n\n### mini-css-extract-plugin\n\nIf you don't want the CSS included in your JS but emit it as external files, you can use the [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin). The media query plugin automatically recognizes the additional CSS chunks and even takes over the plugins filename option!\n\n### html-webpack-plugin\n\nIf you're using the hash feature of webpack (e.g. `[name].[hash].js`) you might also be using the [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) to inject the hashed files into your templates. Good news – the media query plugin supports it! It hooks into the plugin and makes extracted files available in your HTML template via `htmlWebpackPlugin.files.extracted.js` or `htmlWebpackPlugin.files.extracted.css`.\n\nThis let you inject something as `<link rel=\"stylesheet\" href=\"...\" media=\"...\">` so that the extracted files get downloaded but not applied if not necessary (reduces render blocking time). However most of the time it's better to use dynamic imports for the extracted CSS to achieve best performance.\n\nCompared to the regular files (`htmlWebpackPlugin.files.js` or `htmlWebpackPlugin.files.css`) the extracted files object does not have the structure `[file, file]` but `[{file:file,query:query}, {file:file,query:query}]`. Keep this in mind when using it (or check out the example [template](examples/webpack/src/index.hbs)).\n\n## Not using webpack?\n\nThis plugin is built for webpack only and can't be used with other module bundlers (such as [FuseBox](https://fuse-box.org/)) or task runners (such as [Gulp](https://gulpjs.com/)). However if you can't or don't want to use webpack but nevertheless want to extract media queries you should check out my [PostCSS plugin](https://github.com/SassNinja/postcss-extract-media-query) which supports much more tools.\n\nHowever it also breaks out of the bundler/runner and emits files within the PostCSS plugin which will ignore all other pipes in your task.\nSo it's highly recommended to use this webpack plugin instead of the PostCSS alternative!\n\n## Contribution\n\nThis plugin has been built because I wasn't able to find a webpack solution for such a trivial task of splitting files by media query and loading them async. It works for my use cases by I'm pretty sure it can get more improved. So if you miss any feature don't hesitate to create an issue as feature request or to create a PR to do the job.\n\n**And last but not least, if you like this plugin please give it a star on github and share it!**\n\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Complete Examples\n\nIn the following you can find two examples: one using regular webpack (recommended) and another one using [webpack-encore](https://github.com/symfony/webpack-encore) which builds on top of webpack with the goal to make the configuration easier.\n\n**Please note:** these examples are only supposed to show how to use the plugin. They are not meant to be used for your project's assets management without further modification (e.g. dev **and** prod config).\n\n- [webpack](webpack/)\n- [webpack-encore](webpack_encore/) \n"
  },
  {
    "path": "examples/webpack/helpers/if_not_dynamic_import.js",
    "content": "module.exports = function noop(options) {\n  if (!this.file.match(/example-desktop\\.(js|css)/)) {\n    return options.fn(this);\n  }\n};"
  },
  {
    "path": "examples/webpack/package.json",
    "content": "{\n  \"name\": \"media-query-plugin-example\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Complete example for the media-query-plugin.\",\n  \"author\": \"Kai Falkowski\",\n  \"license\": \"MIT\",\n  \"main\": \"webpack.config.js\",\n  \"scripts\": {\n    \"start\": \"rm -rf dist && webpack --config webpack.config.js\",\n    \"debug\": \"rm -rf dist && node --inspect-brk ./node_modules/.bin/webpack --config webpack.config.js\"\n  },\n  \"dependencies\": {},\n  \"devDependencies\": {\n    \"css-loader\": \"^4.3.0\",\n    \"file-loader\": \"^6.1.0\",\n    \"handlebars\": \"^4.7.6\",\n    \"handlebars-loader\": \"^1.7.1\",\n    \"html-webpack-plugin\": \"^5.1.0\",\n    \"media-query-plugin\": \"^1.5.0\",\n    \"mini-css-extract-plugin\": \"^0.11.1\",\n    \"postcss-loader\": \"^4.0.1\",\n    \"sass\": \"^1.26.10\",\n    \"sass-loader\": \"^10.0.2\",\n    \"webpack\": \"^5.21.2\",\n    \"webpack-cli\": \"^4.5.0\"\n  }\n}\n"
  },
  {
    "path": "examples/webpack/postcss.config.js",
    "content": "\nmodule.exports = {};"
  },
  {
    "path": "examples/webpack/src/example-desktop.js",
    "content": "import './example-desktop.scss';"
  },
  {
    "path": "examples/webpack/src/example-desktop.scss",
    "content": "// wrapper for dynamic import"
  },
  {
    "path": "examples/webpack/src/example.js",
    "content": "\nimport './example.scss';\nimport './example2.scss';\n\nconst resizeHandler = () => {\n    if (window.innerWidth >= 960) {\n        import(/* webpackChunkName: 'example-desktop' */ './example-desktop');\n        window.removeEventListener('resize', resizeHandler);\n    }\n};\n\nwindow.addEventListener('resize', resizeHandler);\nresizeHandler();"
  },
  {
    "path": "examples/webpack/src/example.scss",
    "content": ".foo {\n    color: red;\n}\n@media print, screen and (min-width: 60em) {\n    .foo {\n        color: green;\n        color: blue;\n    }\n}\n.bar {\n    font-size: 1rem;\n}\n@media print, screen and (min-width: 60em) and (orientation: landscape) {\n    .bar {\n        font-size: 2rem;\n    }\n}\n.lorem {\n    content: 'ipsum';\n}"
  },
  {
    "path": "examples/webpack/src/example2.scss",
    "content": ".loremipsum {\n    text-decoration: underline;\n}\n@media print, screen and (min-width: 60em) {\n    .loremipsum {\n        text-decoration: none;\n    }\n}\n@media print, screen and (min-width: 60em) {\n    .xyz {\n        text-decoration: underline;\n    }\n}"
  },
  {
    "path": "examples/webpack/src/index.hbs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <title>{{ htmlWebpackPlugin.options.title }}</title>\n{{# each htmlWebpackPlugin.files.css }}\n    <link rel=\"stylesheet\" href=\"{{ this }}\">\n{{/ each }}\n{{# each htmlWebpackPlugin.files.extracted.css }}\n    {{#if_not_dynamic_import}}\n    <link rel=\"stylesheet\" href=\"{{ this.file }}\" media=\"{{ this.query }}\">\n    {{/if_not_dynamic_import}}\n{{/ each }}\n</head>\n<body>\n    <h1 class=\"foo bar\">Hello World</h1>\n    <p class=\"lorem\">Lorem ipsum</p>\n{{# each htmlWebpackPlugin.files.js }}\n    <script src=\"{{ this }}\"></script>\n{{/ each}}\n{{# each htmlWebpackPlugin.files.extracted.js }}\n    {{#if_not_dynamic_import}}\n    <script src=\"{{ this.file }}\"></script>\n    {{/if_not_dynamic_import}}\n{{/ each}}\n</body>\n</html>"
  },
  {
    "path": "examples/webpack/webpack.config.js",
    "content": "\nconst path = require('path');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst MediaQueryPlugin = require('../../src/');\n// const MediaQueryPlugin = require('media-query-plugin');\n\nmodule.exports = {\n    mode: 'development',\n    devtool: 'inline-source-map',\n    entry: {\n        example: './src/example.js'\n    },\n    output: {\n        filename: '[name].js',\n        path: path.resolve(__dirname, 'dist')\n    },\n    module: {\n        rules: [\n            {\n                test: /\\.scss$/,\n                use: [\n                    MiniCssExtractPlugin.loader,\n                    'css-loader',\n                    MediaQueryPlugin.loader,\n                    'postcss-loader',\n                    {\n                        loader: 'sass-loader',\n                        options: {\n                            implementation: require('sass')\n                        }\n                    }\n                ]\n            },\n            {\n                test: /\\.hbs$/,\n                use: [\n                    {\n                        loader: 'handlebars-loader',\n                        options: {\n                            helperDirs: [\n                                path.resolve(__dirname, 'helpers')\n                            ]\n                        }\n                    }\n                ]\n            }\n        ]\n    },\n    optimization: {\n        runtimeChunk: 'single'\n    },\n    plugins: [\n        new HtmlWebpackPlugin({\n            title: 'Media Query Example',\n            template: './src/index.hbs',\n            inject: false\n        }),\n        new MiniCssExtractPlugin({\n            filename: '[name].css'\n        }),\n        new MediaQueryPlugin({\n            include: [\n                'example',\n                'example2'\n            ],\n            queries: {\n                'print, screen and (min-width: 60em)': 'desktop',\n                'print, screen and (min-width: 60em) and (orientation: landscape)': 'desktop'\n            }\n        })\n    ],\n    stats: {\n        children: false\n    }\n};"
  },
  {
    "path": "examples/webpack_encore/package.json",
    "content": "{\n  \"name\": \"media-query-plugin-example-encore\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Complete example for the media-query-plugin with webpack encore.\",\n  \"author\": \"Kai Falkowski\",\n  \"license\": \"MIT\",\n  \"main\": \"webpack.config.js\",\n  \"scripts\": {\n    \"start\": \"encore dev\"\n  },\n  \"dependencies\": {},\n  \"devDependencies\": {\n    \"@symfony/webpack-encore\": \"^1.1.0\",\n    \"media-query-plugin\": \"^1.5.0\",\n    \"postcss-loader\": \"^5.0.0\"\n  }\n}\n"
  },
  {
    "path": "examples/webpack_encore/postcss.config.js",
    "content": "\nmodule.exports = {};"
  },
  {
    "path": "examples/webpack_encore/src/app.js",
    "content": "console.log('hello world');\nimport './app.scss';"
  },
  {
    "path": "examples/webpack_encore/src/app.scss",
    "content": ".foo {\n    color: red;\n}\n@media screen and (min-width: 60em) {\n    .foo {\n        color: green;\n    }\n}\n.bar {\n    font-size: 1rem;\n    &:hover {\n        text-decoration: underline;\n    }\n}\n@media screen and (min-width: 60em) {\n    .bar {\n        font-size: 2rem;\n    }\n}"
  },
  {
    "path": "examples/webpack_encore/webpack.config.js",
    "content": "const Encore = require('@symfony/webpack-encore');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst MediaQueryPlugin = require('../../src/');\n// const MediaQueryPlugin = require('media-query-plugin');\n\nEncore\n    .cleanupOutputBeforeBuild()\n    .enableSingleRuntimeChunk()\n    .setOutputPath('dist/')\n    .setPublicPath('/dist')\n    .addEntry('app', './src/app.js')\n    .enableSourceMaps()\n    .addLoader({ \n        test: /\\.scss$/, \n        use: [\n            MiniCssExtractPlugin.loader,\n            'css-loader',\n            MediaQueryPlugin.loader,\n            'postcss-loader',\n            'sass-loader'\n        ]\n    })\n    .addPlugin(\n        new MiniCssExtractPlugin()\n    )\n    .addPlugin(\n        new MediaQueryPlugin({\n            include: true,\n            queries: {\n                'screen and (min-width: 60em)': 'desktop'\n            }\n        })\n    )\n;\n\nmodule.exports = Encore.getWebpackConfig();"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"media-query-plugin\",\n  \"version\": \"1.5.0\",\n  \"description\": \"Webpack plugin for media query extraction.\",\n  \"author\": \"Kai Falkowski\",\n  \"license\": \"MIT\",\n  \"main\": \"src/index.js\",\n  \"scripts\": {\n    \"test\": \"mocha --timeout 10000\"\n  },\n  \"keywords\": [\n    \"webpack\",\n    \"plugin\",\n    \"loader\",\n    \"mediaquery\",\n    \"extract\",\n    \"split\",\n    \"css\"\n  ],\n  \"dependencies\": {\n    \"loader-utils\": \"^2.0.0\",\n    \"postcss\": \"^7.0.32\",\n    \"webpack\": \"^5.21.2\",\n    \"webpack-sources\": \"^2.2.0\"\n  },\n  \"devDependencies\": {\n    \"chai\": \"^4.2.0\",\n    \"css-loader\": \"^4.3.0\",\n    \"mini-css-extract-plugin\": \"^0.11.1\",\n    \"mocha\": \"^8.1.3\",\n    \"rimraf\": \"^3.0.2\",\n    \"sass\": \"^1.26.10\",\n    \"sass-loader\": \"^10.0.2\",\n    \"style-loader\": \"^1.2.1\",\n    \"webpack-merge\": \"^5.1.4\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/SassNinja/media-query-plugin.git\"\n  }\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "/**\n * Bootstraper that exports the plugin and provides easy access to the loader.\n */\n\nconst loader = require('./loader');\nconst plugin = require('./plugin');\n\n// provide easy access to the loader\nplugin.loader = require.resolve('./loader');\n\n// export default webpack plugin\nmodule.exports = plugin;"
  },
  {
    "path": "src/loader.js",
    "content": "/**\n * The loader component is supposed to extract the media CSS from the source chunks.\n * To do this it uses a custom PostCSS plugin. \n * In the course of this the original media CSS gets removed.\n */\n\nconst { getOptions, interpolateName } = require('loader-utils');\nconst postcss = require('postcss');\nconst store = require('./store');\nconst plugin = require('./postcss');\n\nmodule.exports = function(source) {\n\n    // make loader async\n    const cb = this.async();\n\n    // merge loader's options with plugin's options from store\n    const options = Object.assign(store.options, getOptions(this));\n\n    // basename gets used later to build the key for media query store\n    options.basename = interpolateName(this, '[name]', {});\n\n    // path gets used later to invalidate store (watch mode)\n    // (don't use options.filename to avoid name conflicts)\n    options.path = interpolateName(this, '[path][name].[ext]', {});\n\n    let isIncluded = false;\n\n    // check if current file should be affected\n    if (options.include instanceof Array && options.include.indexOf(options.basename) !== -1) {\n        isIncluded = true;\n    } else if (options.include instanceof RegExp && options.basename.match(options.include)) {\n        isIncluded = true;\n    } else if (options.include === true) {\n        isIncluded = true;\n    }\n\n    // return (either modified or not) source\n    if (isIncluded === true) {\n        postcss([ plugin(options) ])\n            .process(source, { from: options.basename })\n            .then(result => {\n                cb(null, result.toString())\n            });\n    } else {\n        cb(null, source);\n    }\n};"
  },
  {
    "path": "src/plugin.js",
    "content": "/**\n * The plugin component is supposed to inject the extracted media CSS (from store) into the file(s).\n */\n\nconst pluginName = 'MediaQueryPlugin';\n\nconst { OriginalSource, ConcatSource } = require('webpack-sources');\nconst { interpolateName } = require('loader-utils');\nconst Chunk = require('webpack/lib/Chunk');\nconst Compilation = require('webpack/lib/Compilation');\n\nconst store = require('./store');\nconst escapeUtil = require('./utils/escape');\n\nmodule.exports = class MediaQueryPlugin {\n\n    constructor(options) {\n        this.options = Object.assign({\n            include: [],\n            queries: {},\n            groups: {}\n        }, options);\n    }\n\n    getFilenameOption(compiler) {\n        const plugins = compiler.options.plugins;\n        let MiniCssExtractPluginOptions = {};\n\n        for (const plugin of plugins) {\n            if (plugin.constructor.name === 'MiniCssExtractPlugin') {\n                MiniCssExtractPluginOptions = plugin.options || {};\n            }\n        }\n\n        return MiniCssExtractPluginOptions.filename || compiler.options.output.filename;\n    }\n\n    apply(compiler) {\n\n        // if no filename option set, use default\n        this.options.filename = this.options.filename || this.getFilenameOption(compiler);\n\n        // save options in store to provide to loader\n        store.options = this.options;\n\n        // reset store for every webpack instance\n        // required for unit testing because the store is shared\n        compiler.hooks.entryOption.tap(pluginName, () => {\n            store.resetMedia();\n        });\n\n        // if a filename has become invalid (watch mode)\n        // remove all related data from store\n        compiler.hooks.invalid.tap(pluginName, (fileName, changeTime) => {\n            store.removeMediaByFilename(fileName);\n        });\n\n        compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {\n\n            const hasDeprecatedChunks = (compilation.chunks instanceof Set) === false; // webpack < 5\n\n            if (hasDeprecatedChunks) {\n                console.warn('\\n\\n[WARNING] media-query-plugin is going to drop webpack 4 support with the next major version so you should consider upgrading asap!\\n\\n');\n            }\n\n            const processAssets = (compilationAssets, cb) => {\n\n                const chunks = compilation.chunks;\n                const chunkIds = [...chunks].map(chunk => chunk.id);\n                const assets =  hasDeprecatedChunks ? compilation.assets : compilationAssets;\n\n                store.getMediaKeys().forEach(mediaKey => {\n\n                    const css = store.getMedia(mediaKey);\n                    const queries = store.getQueries(mediaKey);\n\n                    // generate hash and use for [hash] within basename\n                    const hash = interpolateName({}, `[hash:${compiler.options.output.hashDigestLength}]`, { content: css });\n\n                    // compute basename according to filename option\n                    // while considering hash\n                    const basename = this.options.filename\n                                        .replace('[name]', mediaKey)\n                                        .replace(/\\[(content|chunk)?hash\\]/, hash)\n                                        .replace(/\\.[^.]+$/, '');\n\n                    // if there's no chunk for the extracted media, create one\n                    if (chunkIds.indexOf(mediaKey) === -1) {\n                        const mediaChunk = new Chunk(mediaKey);\n                        mediaChunk.id = mediaKey;\n                        mediaChunk.ids = [mediaKey];\n\n                        if (hasDeprecatedChunks) {\n                            chunks.push(mediaChunk);\n                        } else {\n                            chunks.add(mediaChunk);\n                        }\n                    }\n\n                    const chunk = [...chunks].filter(chunk => chunk.id === mediaKey)[0];\n\n                    // add query to chunk data if available\n                    // can be used to determine query of a chunk (html-webpack-plugin)\n                    if (queries) {\n                        chunk.query = queries[0];\n                    }\n\n                    // find existing js & css files of this chunk\n                    let existingFiles = { js: [], css: [] };\n                    [...chunk.files].forEach(file => {\n                        if (file.match(/\\.js$/)) {\n                            existingFiles.js.push(file);\n                        } else if (file.match(/\\.css$/)) {\n                            existingFiles.css.push(file);\n                        }\n                    });\n\n                    // if css included in js (style-loader), inject into js\n                    if (existingFiles.js.length > 0 && existingFiles.css.length === 0) {\n                        let content;\n                        const extractedContent = new OriginalSource(`\\nexports.push([module.i, ${escapeUtil(css)}, \"\"]);\\n\\n\\n`, `${basename}.css`);\n\n                        for (let i = 0; i < existingFiles.js.length; i++) {\n                            const file = existingFiles.js[i];\n\n                            if (assets[file]) {\n                                if (i === 0) {\n                                    // since I've to inject the extracted content somewhere into the existing JS code (after `// module`)\n                                    // I can't simply use ConcatSource here but need to split the raw source code first\n                                    // This is only necessary for the first file/iteration\n                                    const originalSource = assets[file].source();\n                                    const aboveContent = new OriginalSource(originalSource.match(/[\\s\\S]*\\/\\/ module/gim)[0], `${basename}.js`);\n                                    const belowContent = new OriginalSource(originalSource.match(/(\\/\\/\\s)?exports[\\s\\S]*/gm)[0], `${basename}.js`);\n\n                                    content = new ConcatSource(aboveContent, extractedContent, belowContent);\n                                } else {\n                                    content = new ConcatSource(assets[file], content);\n                                }\n                                if (hasDeprecatedChunks) {\n                                    chunk.files.splice(chunk.files.indexOf(file), 1);\n                                } else {\n                                    chunk.files.delete(file);\n                                }\n                                delete assets[file];\n                            }\n                        }\n\n                        if (hasDeprecatedChunks) {\n                            chunk.files.push(`${basename}.js`);\n                        } else {\n                            chunk.files.add(`${basename}.js`);\n                        }\n                        assets[`${basename}.js`] = content;\n                    }\n\n                    // else create additional css asset (new chunk)\n                    // or replace existing css assert (mini-css-extract-plugin)\n                    else {\n\n                        let content = new OriginalSource(css, `${basename}.css`);\n                        existingFiles.css.forEach(file => {\n                            if (assets[file]) {\n                                content = new ConcatSource(assets[file], content);\n\n                                if (hasDeprecatedChunks) {\n                                    chunk.files.splice(chunk.files.indexOf(file), 1);\n                                } else {\n                                    chunk.files.delete(file);\n                                }\n                                delete assets[file];\n                            }\n                        });\n\n                        if (hasDeprecatedChunks) {\n                            chunk.files.push(`${basename}.css`);\n                        } else {\n                            chunk.files.add(`${basename}.css`);\n                        }\n                        assets[`${basename}.css`] = content;\n                    }\n\n                });\n\n                // sort assets object for nicer stats and correct order\n                // bcz due to our injection the order got changed\n                const chunksCompareFn = (a, b) => {\n                    if (a.id > b.id)\n                        return 1;\n                    else if (a.id < b.id)\n                        return -1;\n                    else\n                        return 0;\n                };\n                const sortedChunks = [...chunks].sort(chunksCompareFn);\n\n                compilation.chunks = hasDeprecatedChunks ? sortedChunks : new Set(sortedChunks);\n\n                const assetsCompareFn = (a, b) => {\n                    // take file extension out of sort\n                    a = a.replace(/\\.[^.]+$/, '');\n                    b = b.replace(/\\.[^.]+$/, '');\n\n                    if (a > b)\n                        return 1;\n                    else if (a < b)\n                        return -1;\n                    else\n                        return 0;\n                };\n                const sortedAssets = Object.keys(assets).sort(assetsCompareFn).reduce((res, key) => (res[key] = assets[key], res), {});\n\n                if (hasDeprecatedChunks) {\n                    compilation.assets = sortedAssets;\n                } else {\n                    compilationAssets = sortedAssets;\n                }\n\n                cb();\n            };\n\n            // Since webpack 4 doesn't have the processAssets hook, we need the following condition.\n            // In future (once webpack 4 support has been dropped) this can be simplified again.\n            if (hasDeprecatedChunks) {\n                compilation.hooks.additionalAssets.tapAsync(pluginName, (cb) => {\n                    processAssets(compilation.assets, cb);\n                });\n            } else {\n                compilation.hooks.processAssets.tapAsync({\n                    name: pluginName,\n                    stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL\n                }, (assets, cb) => {\n                    processAssets(assets, cb);\n                });\n            }\n\n            // consider html-webpack-plugin and provide extracted files\n            // which can be accessed in templates via htmlWebpackPlugin.files.extracted\n            // { css: [{file:'',query:''},{file:'',query:''}] }\n\n            try {\n                const htmlWebpackPlugin = require('html-webpack-plugin');\n\n                compilation.hooks.afterOptimizeChunkAssets.tap(pluginName, (chunks) => {\n\n                    const hookFn = (pluginArgs, cb) => {\n                        const assetJson = [];\n                        const extracted = {};\n\n                        chunks.forEach(chunk => {\n                            const query = chunk.query;\n\n                            chunk.files.forEach(file => {\n                                const ext = file.match(/\\w+$/)[0];\n\n                                if (query) {\n                                    extracted[ext] = extracted[ext] || [];\n                                    extracted[ext].push({\n                                        file: file,\n                                        query: query\n                                    });\n                                }\n                                assetJson.push(file);\n                            });\n                        });\n\n                        pluginArgs.assets.extracted = extracted;\n                        pluginArgs.plugin.assetJson = JSON.stringify(assetJson);\n                        cb();\n                    };\n\n                    if (htmlWebpackPlugin.getHooks) {\n                        htmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(pluginName, hookFn);\n                    } else if (compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration) {\n                        compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync(pluginName, hookFn);\n                    }\n                });\n            } catch (err) {\n                if (err.code !== 'MODULE_NOT_FOUND') {\n                    throw err;\n                }\n            }\n\n        });\n\n    }\n};\n"
  },
  {
    "path": "src/postcss.js",
    "content": "/**\n * The PostCSS plugin component is supposed to extract the media CSS from the source chunks.\n * The CSS get saved in the store.\n */\n\nconst postcss = require('postcss');\nconst store = require('./store');\nconst normalize = require('./utils/normalize');\n\nmodule.exports = postcss.plugin('MediaQueryPostCSS', options => {\n\n    function addToStore(name, atRule) {\n\n        const css = postcss.root().append(atRule).toString();\n        const query = atRule.params;\n        \n        store.addMedia(name, css, options.path, query);\n    }\n\n    function getGroupName(name) {\n        const groupNames = Object.keys(options.groups);\n\n        for (let i = 0; i < groupNames.length; i++) {\n            const groupName = groupNames[i];\n            const group = options.groups[groupName];\n\n            if (group instanceof RegExp) {\n                if (name.match(group)) {\n                    return groupName;\n                }\n            } else if (Array.isArray(group)) {\n                if (group.includes(name)) {\n                    return groupName;\n                }\n            }\n        }\n    }\n\n    function getQueryName(query) {\n        const queries = Object.keys(options.queries);\n\n        for (let i = 0; i < queries.length; i++) {\n            if (normalize(query) === normalize(queries[i])) {\n                return options.queries[queries[i]];\n            }\n        }\n    }\n\n    return (css, result) => {\n\n        css.walkAtRules('media', atRule => {\n\n            const queryname = getQueryName(atRule.params);\n\n            if (queryname) {\n                const groupName = getGroupName(options.basename);\n                const name = groupName ? `${groupName}-${queryname}` : `${options.basename}-${queryname}`;\n                \n                addToStore(name, atRule);\n                atRule.remove();\n            }\n        });\n    };\n});"
  },
  {
    "path": "src/store.js",
    "content": "/**\n * The store component is supposed to transfer the extracted CSS from the loader to the plugin.\n * Besides it also provides the plugin's options to the loader (as default options).\n */\n\nclass MediaQueryStore {\n\n    constructor() {\n        this.media = {};\n        this.options = {};\n    }\n\n    addMedia(key, css, filename, query) {\n        const data = {\n            css: css,\n            filename: filename,\n            query: query\n        };\n\n        if (typeof this.media[key] !== 'object') {\n            this.media[key] = [];\n        }\n        this.media[key].push(data);\n    }\n\n    getMedia(key) {\n        // create css array from media[key] data\n        // which has the structure [{css:'',filename:'',query:''},{css:'',filename:'',query:''}]\n        const css = this.media[key].map(data => data.css);\n\n        return css.join('\\n');\n    }\n\n    getQueries(key) {\n        // create queries array from media[key] data\n        // which can be used to determine the used query for a key\n        const queries = this.media[key].map(data => data.query);\n\n        return queries;\n    }\n\n    removeMediaByFilename(filename) {\n        this.getMediaKeys().forEach(key => {\n            this.media[key] = this.media[key].filter(media => media.filename !== filename);\n            if (this.media[key].length === 0) {\n                delete this.media[key];\n            }\n        });\n    }\n\n    resetMedia() {\n        this.media = {};\n    }\n\n    getMediaKeys() {\n        return Object.keys(this.media);\n    }\n};\n\nmodule.exports = new MediaQueryStore();"
  },
  {
    "path": "src/utils/escape.js",
    "content": "// Utility to escape a string.\n// Taken over from css-loader\n// https://github.com/webpack-contrib/css-loader/blob/master/lib/url/escape.js\n\nmodule.exports = function escape(url) {\n    if (typeof url !== 'string') {\n        return url\n    }\n    // If url is already wrapped in quotes, remove them\n    if (/^['\"].*['\"]$/.test(url)) {\n        url = url.slice(1, -1);\n    }\n    // Should url be wrapped?\n    // See https://drafts.csswg.org/css-values-3/#urls\n    if (/[\"'() \\t\\n]/.test(url)) {\n        return '\"' + url.replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n') + '\"'\n    }\n\n    return url\n}"
  },
  {
    "path": "src/utils/normalize.js",
    "content": "// Utility to normalize a query for more tolerant comparison.\n// normalize('@media (min-width: 1000px)') === normalize('@media(min-width:1000px)')\n\nmodule.exports = function normalize(query) {\n  return query.toLowerCase().replace(/\\s/g, '');\n}"
  },
  {
    "path": "test/configs/index.js",
    "content": "\nmodule.exports = {\n    'only-javascript-output': require('./webpack/only-javascript-output'),\n    'external-css-output': require('./webpack/external-css-output'),\n    'groups-option-output': require('./webpack/groups-option-output')\n};"
  },
  {
    "path": "test/configs/webpack/base.js",
    "content": "\nmodule.exports = {\n    mode: 'development',\n    devtool: false,\n    entry: {\n        example: './test/data/example.js'\n    },\n    stats: {\n        children: false\n    }\n};"
  },
  {
    "path": "test/configs/webpack/external-css-output.js",
    "content": "\nconst path = require('path');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst MediaQueryPlugin = require('../../../src');\nconst { merge } = require('webpack-merge');\nconst baseConfig = require('./base');\n\nmodule.exports = merge(baseConfig, {\n    output: {\n        filename: '[name].js',\n        path: path.resolve(__dirname, `../../output/external-css-output`)\n    },\n    module: {\n        rules: [\n            {\n                test: /\\.scss$/,\n                use: [\n                    MiniCssExtractPlugin.loader,\n                    'css-loader',\n                    MediaQueryPlugin.loader,\n                    'sass-loader'\n                ]\n            }\n        ]\n    },\n    plugins: [\n        new MiniCssExtractPlugin({\n            filename: '[name].css'\n        }),\n        new MediaQueryPlugin({\n            include: [\n                'example'\n            ],\n            queries: {\n                'print, screen and (max-width: 60em)': 'desktop'\n            }\n        })\n    ]\n});"
  },
  {
    "path": "test/configs/webpack/groups-option-output.js",
    "content": "\nconst path = require('path');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst MediaQueryPlugin = require('../../../src');\n\nmodule.exports = {\n    mode: 'development',\n    entry: {\n        app: './test/data/app.js'\n    },\n    output: {\n        filename: '[name].js',\n        path: path.resolve(__dirname, `../../output/groups-option-output`)\n    },\n    module: {\n        rules: [\n            {\n                test: /\\.scss$/,\n                use: [\n                    MiniCssExtractPlugin.loader,\n                    'css-loader',\n                    MediaQueryPlugin.loader,\n                    'sass-loader'\n                ]\n            }\n        ]\n    },\n    plugins: [\n        new MiniCssExtractPlugin({\n            filename: '[name].css'\n        }),\n        new MediaQueryPlugin({\n            include: true,\n            queries: {\n                'print, screen and (max-width: 60em)': 'desktop'\n            },\n            groups: {\n                app: /^example/\n            }\n        })\n    ],\n    stats: {\n        children: false\n    }\n};"
  },
  {
    "path": "test/configs/webpack/only-javascript-output.js",
    "content": "\nconst path = require('path');\nconst MediaQueryPlugin = require('../../../src');\nconst { merge } = require('webpack-merge');\nconst baseConfig = require('./base');\n\nmodule.exports = merge(baseConfig, {\n    output: {\n        filename: '[name].js',\n        path: path.resolve(__dirname, `../../output/only-javascript-output`)\n    },\n    module: {\n        rules: [\n            {\n                test: /\\.scss$/,\n                use: [\n                    'style-loader',\n                    'css-loader',\n                    MediaQueryPlugin.loader,\n                    'sass-loader'\n                ]\n            }\n        ]\n    },\n    plugins: [\n        new MediaQueryPlugin({\n            include: [\n                'example'\n            ],\n            queries: {\n                'print, screen and (max-width: 60em)': 'desktop'\n            }\n        })\n    ]\n});"
  },
  {
    "path": "test/data/app.js",
    "content": "\n// testing groups option\nimport './exampleA.scss';\nimport './exampleB.scss';"
  },
  {
    "path": "test/data/example-desktop.scss",
    "content": "// wrapper"
  },
  {
    "path": "test/data/example.js",
    "content": "\nimport './example.scss';\n\nif (window.innerWidth >= 960) {\n    import(/* webpackChunkName: 'example-desktop' */ './example-desktop.scss');\n}"
  },
  {
    "path": "test/data/example.scss",
    "content": ".foo {\n    color: red;\n}\n@media print, screen and (max-width: 60em) {\n    .foo {\n        color: green;\n    }\n}\n.bar {\n    font-size: 1rem;\n}"
  },
  {
    "path": "test/data/exampleA.scss",
    "content": ".foo {\n    color: red;\n}\n@media print, screen and (max-width: 60em) {\n    .foo {\n        color: green;\n    }\n}"
  },
  {
    "path": "test/data/exampleB.scss",
    "content": ".bar {\n    font-size: 1rem;\n}\n@media print, screen and (max-width: 60em) {\n    .bar {\n        font-size: 2rem;\n    }\n}"
  },
  {
    "path": "test/webpack-integration.js",
    "content": "\nconst assert = require('chai').assert;\nconst rimraf = require('rimraf');\nconst webpack = require('webpack');\nconst configs = require('./configs');\n\ndescribe('Webpack Integration', function() {\n\n    // clear output before starting any test\n    afterEach(function clearOutput(done) {\n        rimraf('./test/output', done);\n    });\n\n    // test style-loader\n    it('should only emit js files when using style-loader', function(done) {\n\n        const expected = {\n            assets: ['example.js', 'example-desktop.js'],\n            chunks: ['example', 'example-desktop']\n        };\n\n        webpack(configs['only-javascript-output'], (err, stats) => {\n\n            if (err)\n                done(err);\n            else if (stats.hasErrors())\n                done(stats.toString());\n\n            const assets = Object.keys(stats.compilation.assets);\n            const chunks = [...stats.compilation.chunks].map(chunk => chunk.id);\n\n            assert.deepEqual(assets, expected.assets);\n            assert.deepEqual(chunks, expected.chunks);\n            done();\n        });\n\n    });\n\n    // test mini-css-extract-plugin\n    it('should emit css files when using mini-css-extract-plugin', function(done) {\n\n        const expected = {\n            assets: ['example.css', 'example.js', 'example-desktop.js', 'example-desktop.css'],\n            chunks: ['example', 'example-desktop']\n        };\n\n        webpack(configs['external-css-output'], (err, stats) => {\n\n            if (err)\n                done(err);\n            else if (stats.hasErrors())\n                done(stats.toString());\n\n            const assets = Object.keys(stats.compilation.assets);\n            const chunks = [...stats.compilation.chunks].map(chunk => chunk.id);\n\n            assert.deepEqual(assets, expected.assets);\n            assert.deepEqual(chunks, expected.chunks);\n            done();\n        });\n\n    });\n\n    it('should use groups option for extraxted file name', function(done) {\n\n        const expected = {\n            assets: ['app.css', 'app.js', 'app-desktop.css'],\n            chunks: ['app', 'app-desktop']\n        };\n\n        webpack(configs['groups-option-output'], (err, stats) => {\n\n            if (err)\n                done(err);\n            else if (stats.hasErrors())\n                done(stats.toString());\n\n            const assets = Object.keys(stats.compilation.assets);\n            const chunks = [...stats.compilation.chunks].map(chunk => chunk.id);\n\n            assert.deepEqual(assets, expected.assets);\n            assert.deepEqual(chunks, expected.chunks);\n            done();\n        });\n\n    });\n\n});"
  }
]