Full Code of SassNinja/media-query-plugin for AI

master 12b1def71a36 cached
41 files
41.5 KB
10.1k tokens
14 symbols
1 requests
Download .txt
Repository: SassNinja/media-query-plugin
Branch: master
Commit: 12b1def71a36
Files: 41
Total size: 41.5 KB

Directory structure:
gitextract_buyw26nj/

├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── examples/
│   ├── README.md
│   ├── webpack/
│   │   ├── helpers/
│   │   │   └── if_not_dynamic_import.js
│   │   ├── package.json
│   │   ├── postcss.config.js
│   │   ├── src/
│   │   │   ├── example-desktop.js
│   │   │   ├── example-desktop.scss
│   │   │   ├── example.js
│   │   │   ├── example.scss
│   │   │   ├── example2.scss
│   │   │   └── index.hbs
│   │   └── webpack.config.js
│   └── webpack_encore/
│       ├── package.json
│       ├── postcss.config.js
│       ├── src/
│       │   ├── app.js
│       │   └── app.scss
│       └── webpack.config.js
├── package.json
├── src/
│   ├── index.js
│   ├── loader.js
│   ├── plugin.js
│   ├── postcss.js
│   ├── store.js
│   └── utils/
│       ├── escape.js
│       └── normalize.js
└── test/
    ├── configs/
    │   ├── index.js
    │   └── webpack/
    │       ├── base.js
    │       ├── external-css-output.js
    │       ├── groups-option-output.js
    │       └── only-javascript-output.js
    ├── data/
    │   ├── app.js
    │   ├── example-desktop.scss
    │   ├── example.js
    │   ├── example.scss
    │   ├── exampleA.scss
    │   └── exampleB.scss
    └── webpack-integration.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# general
.DS_Store
node_modules
dist

# prefer npm
yarn.lock

# exclude testing output
output

================================================
FILE: .npmignore
================================================
# gitignore
.DS_Store
node_modules
dist
yarn.lock
output

# exclude for npm only
examples
.travis.yml
README.md

================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
  - "lts/*"


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Media Query Plugin

[![Npm Version](https://badge.fury.io/js/media-query-plugin.svg)](https://www.npmjs.com/package/media-query-plugin) 
[![Build Status](https://travis-ci.com/SassNinja/media-query-plugin.svg?branch=master)](https://travis-ci.com/SassNinja/media-query-plugin) 
[![Month Downloads](https://img.shields.io/npm/dm/media-query-plugin.svg)](http://npm-stat.com/charts.html?package=media-query-plugin)

Have you ever thought about extracting your media queries from your CSS so a mobile user doesn't have to load desktop specific CSS?
If so this plugin is what you need!

When 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.

So instead of forcing the user to load this

```css
.foo { color: red }
@media print, screen and (min-width: 75em) {
    .foo { color: blue }
}
.bar { font-size: 1rem }
```

he only has to load this always

```css
.foo { color: red }
.bar { font-size: 1rem }
```

and on desktop viewport size this in addition

```css
@media print, screen and (min-width: 75em) {
    .foo { color: blue }
}
```


## Prerequisites

You 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!

## Installation

Simply install the package with your prefered package manager.

- npm
```bash
npm install media-query-plugin --save-dev
```

- yarn
```bash
yarn add media-query-plugin --dev
```

## Let's get started

### 1. Loader

The plugin comes together with a loader which takes care of the CSS extraction from the source and provides it for the injection afterwards.

**Important:** make sure the loader receives plain CSS so place it between the css-loader and the sass-loader/less-loader.

```javascript
const MediaQueryPlugin = require('media-query-plugin');

module.exports = {
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    MediaQueryPlugin.loader,
                    'postcss-loader',
                    'sass-loader'
                ]
            }
        ]
    }
};
```

### 2. Plugin

Add 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:).

```javascript
const MediaQueryPlugin = require('./plugins/media-query-plugin');

module.exports = {
    plugins: [
        new MediaQueryPlugin({
            include: [
                'example'
            ],
            queries: {
                'print, screen and (min-width: 75em)': 'desktop'
            }
        })
    ]
};
```

### 3. Use Extracted Files

If 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).

**Important:** as mentioned above the name of those files must follow the pattern `[name]-[query]` so an example file could be `example-desktop.scss`

```javascript
import './example.scss';

if (window.innerWidth >= 960) {
    import(/* webpackChunkName: 'example-desktop' */ './example-desktop.scss');
}
```

## Options

The following options are available.

| name        | mandatory |
| ----------- | --------- |
| include     | yes       |
| queries     | yes       |
| groups      | no        |

### include

Each 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.

Possible types
- array (e.g. `['example']`)
- regex (e.g. `/example/`)
- boolean (e.g. `true`)

### queries

This 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.

**Important:** make sure the queries match 100% the source CSS rule excl the `@media`.

**Tip:** you can use the same name for different media queries to concatenate them (e.g. desktop portrait and desktop landscape)

```javascript
queries: {
    'print, screen and (max-width: 60em) and (orientation: portrait)': 'desktop',
    'print, screen and (max-width: 60em) and (orientation: landscape)': 'desktop'
}
```

### groups

By 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]`.
So 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/).

```javascript
groups: {
    app: ['exampleA', 'exampleB']
}
```

**Tip:** you can also use regex to target chunks
```javascript
groups: {
    app: /^example/
}
```

## Other Webpack Plugins

This plugin plays together well with the following other webpack plugins.

### mini-css-extract-plugin

If 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!

### html-webpack-plugin

If 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`.

This 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.

Compared 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)).

## Not using webpack?

This 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.

However it also breaks out of the bundler/runner and emits files within the PostCSS plugin which will ignore all other pipes in your task.
So it's highly recommended to use this webpack plugin instead of the PostCSS alternative!

## Contribution

This 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.

**And last but not least, if you like this plugin please give it a star on github and share it!**



================================================
FILE: examples/README.md
================================================
# Complete Examples

In 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.

**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).

- [webpack](webpack/)
- [webpack-encore](webpack_encore/) 


================================================
FILE: examples/webpack/helpers/if_not_dynamic_import.js
================================================
module.exports = function noop(options) {
  if (!this.file.match(/example-desktop\.(js|css)/)) {
    return options.fn(this);
  }
};

================================================
FILE: examples/webpack/package.json
================================================
{
  "name": "media-query-plugin-example",
  "version": "1.0.0",
  "description": "Complete example for the media-query-plugin.",
  "author": "Kai Falkowski",
  "license": "MIT",
  "main": "webpack.config.js",
  "scripts": {
    "start": "rm -rf dist && webpack --config webpack.config.js",
    "debug": "rm -rf dist && node --inspect-brk ./node_modules/.bin/webpack --config webpack.config.js"
  },
  "dependencies": {},
  "devDependencies": {
    "css-loader": "^4.3.0",
    "file-loader": "^6.1.0",
    "handlebars": "^4.7.6",
    "handlebars-loader": "^1.7.1",
    "html-webpack-plugin": "^5.1.0",
    "media-query-plugin": "^1.5.0",
    "mini-css-extract-plugin": "^0.11.1",
    "postcss-loader": "^4.0.1",
    "sass": "^1.26.10",
    "sass-loader": "^10.0.2",
    "webpack": "^5.21.2",
    "webpack-cli": "^4.5.0"
  }
}


================================================
FILE: examples/webpack/postcss.config.js
================================================

module.exports = {};

================================================
FILE: examples/webpack/src/example-desktop.js
================================================
import './example-desktop.scss';

================================================
FILE: examples/webpack/src/example-desktop.scss
================================================
// wrapper for dynamic import

================================================
FILE: examples/webpack/src/example.js
================================================

import './example.scss';
import './example2.scss';

const resizeHandler = () => {
    if (window.innerWidth >= 960) {
        import(/* webpackChunkName: 'example-desktop' */ './example-desktop');
        window.removeEventListener('resize', resizeHandler);
    }
};

window.addEventListener('resize', resizeHandler);
resizeHandler();

================================================
FILE: examples/webpack/src/example.scss
================================================
.foo {
    color: red;
}
@media print, screen and (min-width: 60em) {
    .foo {
        color: green;
        color: blue;
    }
}
.bar {
    font-size: 1rem;
}
@media print, screen and (min-width: 60em) and (orientation: landscape) {
    .bar {
        font-size: 2rem;
    }
}
.lorem {
    content: 'ipsum';
}

================================================
FILE: examples/webpack/src/example2.scss
================================================
.loremipsum {
    text-decoration: underline;
}
@media print, screen and (min-width: 60em) {
    .loremipsum {
        text-decoration: none;
    }
}
@media print, screen and (min-width: 60em) {
    .xyz {
        text-decoration: underline;
    }
}

================================================
FILE: examples/webpack/src/index.hbs
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{{ htmlWebpackPlugin.options.title }}</title>
{{# each htmlWebpackPlugin.files.css }}
    <link rel="stylesheet" href="{{ this }}">
{{/ each }}
{{# each htmlWebpackPlugin.files.extracted.css }}
    {{#if_not_dynamic_import}}
    <link rel="stylesheet" href="{{ this.file }}" media="{{ this.query }}">
    {{/if_not_dynamic_import}}
{{/ each }}
</head>
<body>
    <h1 class="foo bar">Hello World</h1>
    <p class="lorem">Lorem ipsum</p>
{{# each htmlWebpackPlugin.files.js }}
    <script src="{{ this }}"></script>
{{/ each}}
{{# each htmlWebpackPlugin.files.extracted.js }}
    {{#if_not_dynamic_import}}
    <script src="{{ this.file }}"></script>
    {{/if_not_dynamic_import}}
{{/ each}}
</body>
</html>

================================================
FILE: examples/webpack/webpack.config.js
================================================

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const MediaQueryPlugin = require('../../src/');
// const MediaQueryPlugin = require('media-query-plugin');

module.exports = {
    mode: 'development',
    devtool: 'inline-source-map',
    entry: {
        example: './src/example.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    MediaQueryPlugin.loader,
                    'postcss-loader',
                    {
                        loader: 'sass-loader',
                        options: {
                            implementation: require('sass')
                        }
                    }
                ]
            },
            {
                test: /\.hbs$/,
                use: [
                    {
                        loader: 'handlebars-loader',
                        options: {
                            helperDirs: [
                                path.resolve(__dirname, 'helpers')
                            ]
                        }
                    }
                ]
            }
        ]
    },
    optimization: {
        runtimeChunk: 'single'
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: 'Media Query Example',
            template: './src/index.hbs',
            inject: false
        }),
        new MiniCssExtractPlugin({
            filename: '[name].css'
        }),
        new MediaQueryPlugin({
            include: [
                'example',
                'example2'
            ],
            queries: {
                'print, screen and (min-width: 60em)': 'desktop',
                'print, screen and (min-width: 60em) and (orientation: landscape)': 'desktop'
            }
        })
    ],
    stats: {
        children: false
    }
};

================================================
FILE: examples/webpack_encore/package.json
================================================
{
  "name": "media-query-plugin-example-encore",
  "version": "1.0.0",
  "description": "Complete example for the media-query-plugin with webpack encore.",
  "author": "Kai Falkowski",
  "license": "MIT",
  "main": "webpack.config.js",
  "scripts": {
    "start": "encore dev"
  },
  "dependencies": {},
  "devDependencies": {
    "@symfony/webpack-encore": "^1.1.0",
    "media-query-plugin": "^1.5.0",
    "postcss-loader": "^5.0.0"
  }
}


================================================
FILE: examples/webpack_encore/postcss.config.js
================================================

module.exports = {};

================================================
FILE: examples/webpack_encore/src/app.js
================================================
console.log('hello world');
import './app.scss';

================================================
FILE: examples/webpack_encore/src/app.scss
================================================
.foo {
    color: red;
}
@media screen and (min-width: 60em) {
    .foo {
        color: green;
    }
}
.bar {
    font-size: 1rem;
    &:hover {
        text-decoration: underline;
    }
}
@media screen and (min-width: 60em) {
    .bar {
        font-size: 2rem;
    }
}

================================================
FILE: examples/webpack_encore/webpack.config.js
================================================
const Encore = require('@symfony/webpack-encore');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const MediaQueryPlugin = require('../../src/');
// const MediaQueryPlugin = require('media-query-plugin');

Encore
    .cleanupOutputBeforeBuild()
    .enableSingleRuntimeChunk()
    .setOutputPath('dist/')
    .setPublicPath('/dist')
    .addEntry('app', './src/app.js')
    .enableSourceMaps()
    .addLoader({ 
        test: /\.scss$/, 
        use: [
            MiniCssExtractPlugin.loader,
            'css-loader',
            MediaQueryPlugin.loader,
            'postcss-loader',
            'sass-loader'
        ]
    })
    .addPlugin(
        new MiniCssExtractPlugin()
    )
    .addPlugin(
        new MediaQueryPlugin({
            include: true,
            queries: {
                'screen and (min-width: 60em)': 'desktop'
            }
        })
    )
;

module.exports = Encore.getWebpackConfig();

================================================
FILE: package.json
================================================
{
  "name": "media-query-plugin",
  "version": "1.5.0",
  "description": "Webpack plugin for media query extraction.",
  "author": "Kai Falkowski",
  "license": "MIT",
  "main": "src/index.js",
  "scripts": {
    "test": "mocha --timeout 10000"
  },
  "keywords": [
    "webpack",
    "plugin",
    "loader",
    "mediaquery",
    "extract",
    "split",
    "css"
  ],
  "dependencies": {
    "loader-utils": "^2.0.0",
    "postcss": "^7.0.32",
    "webpack": "^5.21.2",
    "webpack-sources": "^2.2.0"
  },
  "devDependencies": {
    "chai": "^4.2.0",
    "css-loader": "^4.3.0",
    "mini-css-extract-plugin": "^0.11.1",
    "mocha": "^8.1.3",
    "rimraf": "^3.0.2",
    "sass": "^1.26.10",
    "sass-loader": "^10.0.2",
    "style-loader": "^1.2.1",
    "webpack-merge": "^5.1.4"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/SassNinja/media-query-plugin.git"
  }
}


================================================
FILE: src/index.js
================================================
/**
 * Bootstraper that exports the plugin and provides easy access to the loader.
 */

const loader = require('./loader');
const plugin = require('./plugin');

// provide easy access to the loader
plugin.loader = require.resolve('./loader');

// export default webpack plugin
module.exports = plugin;

================================================
FILE: src/loader.js
================================================
/**
 * The loader component is supposed to extract the media CSS from the source chunks.
 * To do this it uses a custom PostCSS plugin. 
 * In the course of this the original media CSS gets removed.
 */

const { getOptions, interpolateName } = require('loader-utils');
const postcss = require('postcss');
const store = require('./store');
const plugin = require('./postcss');

module.exports = function(source) {

    // make loader async
    const cb = this.async();

    // merge loader's options with plugin's options from store
    const options = Object.assign(store.options, getOptions(this));

    // basename gets used later to build the key for media query store
    options.basename = interpolateName(this, '[name]', {});

    // path gets used later to invalidate store (watch mode)
    // (don't use options.filename to avoid name conflicts)
    options.path = interpolateName(this, '[path][name].[ext]', {});

    let isIncluded = false;

    // check if current file should be affected
    if (options.include instanceof Array && options.include.indexOf(options.basename) !== -1) {
        isIncluded = true;
    } else if (options.include instanceof RegExp && options.basename.match(options.include)) {
        isIncluded = true;
    } else if (options.include === true) {
        isIncluded = true;
    }

    // return (either modified or not) source
    if (isIncluded === true) {
        postcss([ plugin(options) ])
            .process(source, { from: options.basename })
            .then(result => {
                cb(null, result.toString())
            });
    } else {
        cb(null, source);
    }
};

================================================
FILE: src/plugin.js
================================================
/**
 * The plugin component is supposed to inject the extracted media CSS (from store) into the file(s).
 */

const pluginName = 'MediaQueryPlugin';

const { OriginalSource, ConcatSource } = require('webpack-sources');
const { interpolateName } = require('loader-utils');
const Chunk = require('webpack/lib/Chunk');
const Compilation = require('webpack/lib/Compilation');

const store = require('./store');
const escapeUtil = require('./utils/escape');

module.exports = class MediaQueryPlugin {

    constructor(options) {
        this.options = Object.assign({
            include: [],
            queries: {},
            groups: {}
        }, options);
    }

    getFilenameOption(compiler) {
        const plugins = compiler.options.plugins;
        let MiniCssExtractPluginOptions = {};

        for (const plugin of plugins) {
            if (plugin.constructor.name === 'MiniCssExtractPlugin') {
                MiniCssExtractPluginOptions = plugin.options || {};
            }
        }

        return MiniCssExtractPluginOptions.filename || compiler.options.output.filename;
    }

    apply(compiler) {

        // if no filename option set, use default
        this.options.filename = this.options.filename || this.getFilenameOption(compiler);

        // save options in store to provide to loader
        store.options = this.options;

        // reset store for every webpack instance
        // required for unit testing because the store is shared
        compiler.hooks.entryOption.tap(pluginName, () => {
            store.resetMedia();
        });

        // if a filename has become invalid (watch mode)
        // remove all related data from store
        compiler.hooks.invalid.tap(pluginName, (fileName, changeTime) => {
            store.removeMediaByFilename(fileName);
        });

        compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {

            const hasDeprecatedChunks = (compilation.chunks instanceof Set) === false; // webpack < 5

            if (hasDeprecatedChunks) {
                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');
            }

            const processAssets = (compilationAssets, cb) => {

                const chunks = compilation.chunks;
                const chunkIds = [...chunks].map(chunk => chunk.id);
                const assets =  hasDeprecatedChunks ? compilation.assets : compilationAssets;

                store.getMediaKeys().forEach(mediaKey => {

                    const css = store.getMedia(mediaKey);
                    const queries = store.getQueries(mediaKey);

                    // generate hash and use for [hash] within basename
                    const hash = interpolateName({}, `[hash:${compiler.options.output.hashDigestLength}]`, { content: css });

                    // compute basename according to filename option
                    // while considering hash
                    const basename = this.options.filename
                                        .replace('[name]', mediaKey)
                                        .replace(/\[(content|chunk)?hash\]/, hash)
                                        .replace(/\.[^.]+$/, '');

                    // if there's no chunk for the extracted media, create one
                    if (chunkIds.indexOf(mediaKey) === -1) {
                        const mediaChunk = new Chunk(mediaKey);
                        mediaChunk.id = mediaKey;
                        mediaChunk.ids = [mediaKey];

                        if (hasDeprecatedChunks) {
                            chunks.push(mediaChunk);
                        } else {
                            chunks.add(mediaChunk);
                        }
                    }

                    const chunk = [...chunks].filter(chunk => chunk.id === mediaKey)[0];

                    // add query to chunk data if available
                    // can be used to determine query of a chunk (html-webpack-plugin)
                    if (queries) {
                        chunk.query = queries[0];
                    }

                    // find existing js & css files of this chunk
                    let existingFiles = { js: [], css: [] };
                    [...chunk.files].forEach(file => {
                        if (file.match(/\.js$/)) {
                            existingFiles.js.push(file);
                        } else if (file.match(/\.css$/)) {
                            existingFiles.css.push(file);
                        }
                    });

                    // if css included in js (style-loader), inject into js
                    if (existingFiles.js.length > 0 && existingFiles.css.length === 0) {
                        let content;
                        const extractedContent = new OriginalSource(`\nexports.push([module.i, ${escapeUtil(css)}, ""]);\n\n\n`, `${basename}.css`);

                        for (let i = 0; i < existingFiles.js.length; i++) {
                            const file = existingFiles.js[i];

                            if (assets[file]) {
                                if (i === 0) {
                                    // since I've to inject the extracted content somewhere into the existing JS code (after `// module`)
                                    // I can't simply use ConcatSource here but need to split the raw source code first
                                    // This is only necessary for the first file/iteration
                                    const originalSource = assets[file].source();
                                    const aboveContent = new OriginalSource(originalSource.match(/[\s\S]*\/\/ module/gim)[0], `${basename}.js`);
                                    const belowContent = new OriginalSource(originalSource.match(/(\/\/\s)?exports[\s\S]*/gm)[0], `${basename}.js`);

                                    content = new ConcatSource(aboveContent, extractedContent, belowContent);
                                } else {
                                    content = new ConcatSource(assets[file], content);
                                }
                                if (hasDeprecatedChunks) {
                                    chunk.files.splice(chunk.files.indexOf(file), 1);
                                } else {
                                    chunk.files.delete(file);
                                }
                                delete assets[file];
                            }
                        }

                        if (hasDeprecatedChunks) {
                            chunk.files.push(`${basename}.js`);
                        } else {
                            chunk.files.add(`${basename}.js`);
                        }
                        assets[`${basename}.js`] = content;
                    }

                    // else create additional css asset (new chunk)
                    // or replace existing css assert (mini-css-extract-plugin)
                    else {

                        let content = new OriginalSource(css, `${basename}.css`);
                        existingFiles.css.forEach(file => {
                            if (assets[file]) {
                                content = new ConcatSource(assets[file], content);

                                if (hasDeprecatedChunks) {
                                    chunk.files.splice(chunk.files.indexOf(file), 1);
                                } else {
                                    chunk.files.delete(file);
                                }
                                delete assets[file];
                            }
                        });

                        if (hasDeprecatedChunks) {
                            chunk.files.push(`${basename}.css`);
                        } else {
                            chunk.files.add(`${basename}.css`);
                        }
                        assets[`${basename}.css`] = content;
                    }

                });

                // sort assets object for nicer stats and correct order
                // bcz due to our injection the order got changed
                const chunksCompareFn = (a, b) => {
                    if (a.id > b.id)
                        return 1;
                    else if (a.id < b.id)
                        return -1;
                    else
                        return 0;
                };
                const sortedChunks = [...chunks].sort(chunksCompareFn);

                compilation.chunks = hasDeprecatedChunks ? sortedChunks : new Set(sortedChunks);

                const assetsCompareFn = (a, b) => {
                    // take file extension out of sort
                    a = a.replace(/\.[^.]+$/, '');
                    b = b.replace(/\.[^.]+$/, '');

                    if (a > b)
                        return 1;
                    else if (a < b)
                        return -1;
                    else
                        return 0;
                };
                const sortedAssets = Object.keys(assets).sort(assetsCompareFn).reduce((res, key) => (res[key] = assets[key], res), {});

                if (hasDeprecatedChunks) {
                    compilation.assets = sortedAssets;
                } else {
                    compilationAssets = sortedAssets;
                }

                cb();
            };

            // Since webpack 4 doesn't have the processAssets hook, we need the following condition.
            // In future (once webpack 4 support has been dropped) this can be simplified again.
            if (hasDeprecatedChunks) {
                compilation.hooks.additionalAssets.tapAsync(pluginName, (cb) => {
                    processAssets(compilation.assets, cb);
                });
            } else {
                compilation.hooks.processAssets.tapAsync({
                    name: pluginName,
                    stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
                }, (assets, cb) => {
                    processAssets(assets, cb);
                });
            }

            // consider html-webpack-plugin and provide extracted files
            // which can be accessed in templates via htmlWebpackPlugin.files.extracted
            // { css: [{file:'',query:''},{file:'',query:''}] }

            try {
                const htmlWebpackPlugin = require('html-webpack-plugin');

                compilation.hooks.afterOptimizeChunkAssets.tap(pluginName, (chunks) => {

                    const hookFn = (pluginArgs, cb) => {
                        const assetJson = [];
                        const extracted = {};

                        chunks.forEach(chunk => {
                            const query = chunk.query;

                            chunk.files.forEach(file => {
                                const ext = file.match(/\w+$/)[0];

                                if (query) {
                                    extracted[ext] = extracted[ext] || [];
                                    extracted[ext].push({
                                        file: file,
                                        query: query
                                    });
                                }
                                assetJson.push(file);
                            });
                        });

                        pluginArgs.assets.extracted = extracted;
                        pluginArgs.plugin.assetJson = JSON.stringify(assetJson);
                        cb();
                    };

                    if (htmlWebpackPlugin.getHooks) {
                        htmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(pluginName, hookFn);
                    } else if (compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration) {
                        compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync(pluginName, hookFn);
                    }
                });
            } catch (err) {
                if (err.code !== 'MODULE_NOT_FOUND') {
                    throw err;
                }
            }

        });

    }
};


================================================
FILE: src/postcss.js
================================================
/**
 * The PostCSS plugin component is supposed to extract the media CSS from the source chunks.
 * The CSS get saved in the store.
 */

const postcss = require('postcss');
const store = require('./store');
const normalize = require('./utils/normalize');

module.exports = postcss.plugin('MediaQueryPostCSS', options => {

    function addToStore(name, atRule) {

        const css = postcss.root().append(atRule).toString();
        const query = atRule.params;
        
        store.addMedia(name, css, options.path, query);
    }

    function getGroupName(name) {
        const groupNames = Object.keys(options.groups);

        for (let i = 0; i < groupNames.length; i++) {
            const groupName = groupNames[i];
            const group = options.groups[groupName];

            if (group instanceof RegExp) {
                if (name.match(group)) {
                    return groupName;
                }
            } else if (Array.isArray(group)) {
                if (group.includes(name)) {
                    return groupName;
                }
            }
        }
    }

    function getQueryName(query) {
        const queries = Object.keys(options.queries);

        for (let i = 0; i < queries.length; i++) {
            if (normalize(query) === normalize(queries[i])) {
                return options.queries[queries[i]];
            }
        }
    }

    return (css, result) => {

        css.walkAtRules('media', atRule => {

            const queryname = getQueryName(atRule.params);

            if (queryname) {
                const groupName = getGroupName(options.basename);
                const name = groupName ? `${groupName}-${queryname}` : `${options.basename}-${queryname}`;
                
                addToStore(name, atRule);
                atRule.remove();
            }
        });
    };
});

================================================
FILE: src/store.js
================================================
/**
 * The store component is supposed to transfer the extracted CSS from the loader to the plugin.
 * Besides it also provides the plugin's options to the loader (as default options).
 */

class MediaQueryStore {

    constructor() {
        this.media = {};
        this.options = {};
    }

    addMedia(key, css, filename, query) {
        const data = {
            css: css,
            filename: filename,
            query: query
        };

        if (typeof this.media[key] !== 'object') {
            this.media[key] = [];
        }
        this.media[key].push(data);
    }

    getMedia(key) {
        // create css array from media[key] data
        // which has the structure [{css:'',filename:'',query:''},{css:'',filename:'',query:''}]
        const css = this.media[key].map(data => data.css);

        return css.join('\n');
    }

    getQueries(key) {
        // create queries array from media[key] data
        // which can be used to determine the used query for a key
        const queries = this.media[key].map(data => data.query);

        return queries;
    }

    removeMediaByFilename(filename) {
        this.getMediaKeys().forEach(key => {
            this.media[key] = this.media[key].filter(media => media.filename !== filename);
            if (this.media[key].length === 0) {
                delete this.media[key];
            }
        });
    }

    resetMedia() {
        this.media = {};
    }

    getMediaKeys() {
        return Object.keys(this.media);
    }
};

module.exports = new MediaQueryStore();

================================================
FILE: src/utils/escape.js
================================================
// Utility to escape a string.
// Taken over from css-loader
// https://github.com/webpack-contrib/css-loader/blob/master/lib/url/escape.js

module.exports = function escape(url) {
    if (typeof url !== 'string') {
        return url
    }
    // If url is already wrapped in quotes, remove them
    if (/^['"].*['"]$/.test(url)) {
        url = url.slice(1, -1);
    }
    // Should url be wrapped?
    // See https://drafts.csswg.org/css-values-3/#urls
    if (/["'() \t\n]/.test(url)) {
        return '"' + url.replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"'
    }

    return url
}

================================================
FILE: src/utils/normalize.js
================================================
// Utility to normalize a query for more tolerant comparison.
// normalize('@media (min-width: 1000px)') === normalize('@media(min-width:1000px)')

module.exports = function normalize(query) {
  return query.toLowerCase().replace(/\s/g, '');
}

================================================
FILE: test/configs/index.js
================================================

module.exports = {
    'only-javascript-output': require('./webpack/only-javascript-output'),
    'external-css-output': require('./webpack/external-css-output'),
    'groups-option-output': require('./webpack/groups-option-output')
};

================================================
FILE: test/configs/webpack/base.js
================================================

module.exports = {
    mode: 'development',
    devtool: false,
    entry: {
        example: './test/data/example.js'
    },
    stats: {
        children: false
    }
};

================================================
FILE: test/configs/webpack/external-css-output.js
================================================

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const MediaQueryPlugin = require('../../../src');
const { merge } = require('webpack-merge');
const baseConfig = require('./base');

module.exports = merge(baseConfig, {
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, `../../output/external-css-output`)
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    MediaQueryPlugin.loader,
                    'sass-loader'
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css'
        }),
        new MediaQueryPlugin({
            include: [
                'example'
            ],
            queries: {
                'print, screen and (max-width: 60em)': 'desktop'
            }
        })
    ]
});

================================================
FILE: test/configs/webpack/groups-option-output.js
================================================

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const MediaQueryPlugin = require('../../../src');

module.exports = {
    mode: 'development',
    entry: {
        app: './test/data/app.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, `../../output/groups-option-output`)
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    MediaQueryPlugin.loader,
                    'sass-loader'
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css'
        }),
        new MediaQueryPlugin({
            include: true,
            queries: {
                'print, screen and (max-width: 60em)': 'desktop'
            },
            groups: {
                app: /^example/
            }
        })
    ],
    stats: {
        children: false
    }
};

================================================
FILE: test/configs/webpack/only-javascript-output.js
================================================

const path = require('path');
const MediaQueryPlugin = require('../../../src');
const { merge } = require('webpack-merge');
const baseConfig = require('./base');

module.exports = merge(baseConfig, {
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, `../../output/only-javascript-output`)
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    MediaQueryPlugin.loader,
                    'sass-loader'
                ]
            }
        ]
    },
    plugins: [
        new MediaQueryPlugin({
            include: [
                'example'
            ],
            queries: {
                'print, screen and (max-width: 60em)': 'desktop'
            }
        })
    ]
});

================================================
FILE: test/data/app.js
================================================

// testing groups option
import './exampleA.scss';
import './exampleB.scss';

================================================
FILE: test/data/example-desktop.scss
================================================
// wrapper

================================================
FILE: test/data/example.js
================================================

import './example.scss';

if (window.innerWidth >= 960) {
    import(/* webpackChunkName: 'example-desktop' */ './example-desktop.scss');
}

================================================
FILE: test/data/example.scss
================================================
.foo {
    color: red;
}
@media print, screen and (max-width: 60em) {
    .foo {
        color: green;
    }
}
.bar {
    font-size: 1rem;
}

================================================
FILE: test/data/exampleA.scss
================================================
.foo {
    color: red;
}
@media print, screen and (max-width: 60em) {
    .foo {
        color: green;
    }
}

================================================
FILE: test/data/exampleB.scss
================================================
.bar {
    font-size: 1rem;
}
@media print, screen and (max-width: 60em) {
    .bar {
        font-size: 2rem;
    }
}

================================================
FILE: test/webpack-integration.js
================================================

const assert = require('chai').assert;
const rimraf = require('rimraf');
const webpack = require('webpack');
const configs = require('./configs');

describe('Webpack Integration', function() {

    // clear output before starting any test
    afterEach(function clearOutput(done) {
        rimraf('./test/output', done);
    });

    // test style-loader
    it('should only emit js files when using style-loader', function(done) {

        const expected = {
            assets: ['example.js', 'example-desktop.js'],
            chunks: ['example', 'example-desktop']
        };

        webpack(configs['only-javascript-output'], (err, stats) => {

            if (err)
                done(err);
            else if (stats.hasErrors())
                done(stats.toString());

            const assets = Object.keys(stats.compilation.assets);
            const chunks = [...stats.compilation.chunks].map(chunk => chunk.id);

            assert.deepEqual(assets, expected.assets);
            assert.deepEqual(chunks, expected.chunks);
            done();
        });

    });

    // test mini-css-extract-plugin
    it('should emit css files when using mini-css-extract-plugin', function(done) {

        const expected = {
            assets: ['example.css', 'example.js', 'example-desktop.js', 'example-desktop.css'],
            chunks: ['example', 'example-desktop']
        };

        webpack(configs['external-css-output'], (err, stats) => {

            if (err)
                done(err);
            else if (stats.hasErrors())
                done(stats.toString());

            const assets = Object.keys(stats.compilation.assets);
            const chunks = [...stats.compilation.chunks].map(chunk => chunk.id);

            assert.deepEqual(assets, expected.assets);
            assert.deepEqual(chunks, expected.chunks);
            done();
        });

    });

    it('should use groups option for extraxted file name', function(done) {

        const expected = {
            assets: ['app.css', 'app.js', 'app-desktop.css'],
            chunks: ['app', 'app-desktop']
        };

        webpack(configs['groups-option-output'], (err, stats) => {

            if (err)
                done(err);
            else if (stats.hasErrors())
                done(stats.toString());

            const assets = Object.keys(stats.compilation.assets);
            const chunks = [...stats.compilation.chunks].map(chunk => chunk.id);

            assert.deepEqual(assets, expected.assets);
            assert.deepEqual(chunks, expected.chunks);
            done();
        });

    });

});
Download .txt
gitextract_buyw26nj/

├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── examples/
│   ├── README.md
│   ├── webpack/
│   │   ├── helpers/
│   │   │   └── if_not_dynamic_import.js
│   │   ├── package.json
│   │   ├── postcss.config.js
│   │   ├── src/
│   │   │   ├── example-desktop.js
│   │   │   ├── example-desktop.scss
│   │   │   ├── example.js
│   │   │   ├── example.scss
│   │   │   ├── example2.scss
│   │   │   └── index.hbs
│   │   └── webpack.config.js
│   └── webpack_encore/
│       ├── package.json
│       ├── postcss.config.js
│       ├── src/
│       │   ├── app.js
│       │   └── app.scss
│       └── webpack.config.js
├── package.json
├── src/
│   ├── index.js
│   ├── loader.js
│   ├── plugin.js
│   ├── postcss.js
│   ├── store.js
│   └── utils/
│       ├── escape.js
│       └── normalize.js
└── test/
    ├── configs/
    │   ├── index.js
    │   └── webpack/
    │       ├── base.js
    │       ├── external-css-output.js
    │       ├── groups-option-output.js
    │       └── only-javascript-output.js
    ├── data/
    │   ├── app.js
    │   ├── example-desktop.scss
    │   ├── example.js
    │   ├── example.scss
    │   ├── exampleA.scss
    │   └── exampleB.scss
    └── webpack-integration.js
Download .txt
SYMBOL INDEX (14 symbols across 3 files)

FILE: src/plugin.js
  method constructor (line 17) | constructor(options) {
  method getFilenameOption (line 25) | getFilenameOption(compiler) {
  method apply (line 38) | apply(compiler) {

FILE: src/postcss.js
  function addToStore (line 12) | function addToStore(name, atRule) {
  function getGroupName (line 20) | function getGroupName(name) {
  function getQueryName (line 39) | function getQueryName(query) {

FILE: src/store.js
  class MediaQueryStore (line 6) | class MediaQueryStore {
    method constructor (line 8) | constructor() {
    method addMedia (line 13) | addMedia(key, css, filename, query) {
    method getMedia (line 26) | getMedia(key) {
    method getQueries (line 34) | getQueries(key) {
    method removeMediaByFilename (line 42) | removeMediaByFilename(filename) {
    method resetMedia (line 51) | resetMedia() {
    method getMediaKeys (line 55) | getMediaKeys() {
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (46K chars).
[
  {
    "path": ".gitignore",
    "chars": 94,
    "preview": "# general\n.DS_Store\nnode_modules\ndist\n\n# prefer npm\nyarn.lock\n\n# exclude testing output\noutput"
  },
  {
    "path": ".npmignore",
    "chars": 111,
    "preview": "# 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",
    "chars": 39,
    "preview": "language: node_js\nnode_js:\n  - \"lts/*\"\n"
  },
  {
    "path": "LICENSE",
    "chars": 1057,
    "preview": "MIT License\n\nCopyright (c) 2018 \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "README.md",
    "chars": 8285,
    "preview": "# Media Query Plugin\n\n[![Npm Version](https://badge.fury.io/js/media-query-plugin.svg)](https://www.npmjs.com/package/me"
  },
  {
    "path": "examples/README.md",
    "chars": 536,
    "preview": "# Complete Examples\n\nIn the following you can find two examples: one using regular webpack (recommended) and another one"
  },
  {
    "path": "examples/webpack/helpers/if_not_dynamic_import.js",
    "chars": 132,
    "preview": "module.exports = function noop(options) {\n  if (!this.file.match(/example-desktop\\.(js|css)/)) {\n    return options.fn(t"
  },
  {
    "path": "examples/webpack/package.json",
    "chars": 825,
    "preview": "{\n  \"name\": \"media-query-plugin-example\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Complete example for the media-query-p"
  },
  {
    "path": "examples/webpack/postcss.config.js",
    "chars": 21,
    "preview": "\nmodule.exports = {};"
  },
  {
    "path": "examples/webpack/src/example-desktop.js",
    "chars": 32,
    "preview": "import './example-desktop.scss';"
  },
  {
    "path": "examples/webpack/src/example-desktop.scss",
    "chars": 29,
    "preview": "// wrapper for dynamic import"
  },
  {
    "path": "examples/webpack/src/example.js",
    "chars": 335,
    "preview": "\nimport './example.scss';\nimport './example2.scss';\n\nconst resizeHandler = () => {\n    if (window.innerWidth >= 960) {\n "
  },
  {
    "path": "examples/webpack/src/example.scss",
    "chars": 312,
    "preview": ".foo {\n    color: red;\n}\n@media print, screen and (min-width: 60em) {\n    .foo {\n        color: green;\n        color: bl"
  },
  {
    "path": "examples/webpack/src/example2.scss",
    "chars": 249,
    "preview": ".loremipsum {\n    text-decoration: underline;\n}\n@media print, screen and (min-width: 60em) {\n    .loremipsum {\n        t"
  },
  {
    "path": "examples/webpack/src/index.hbs",
    "chars": 785,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <title>{{ htmlWebpackPlugin.options.title }}</tit"
  },
  {
    "path": "examples/webpack/webpack.config.js",
    "chars": 2112,
    "preview": "\nconst path = require('path');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\nconst MiniCssExtractPlugin = re"
  },
  {
    "path": "examples/webpack_encore/package.json",
    "chars": 441,
    "preview": "{\n  \"name\": \"media-query-plugin-example-encore\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Complete example for the media-"
  },
  {
    "path": "examples/webpack_encore/postcss.config.js",
    "chars": 21,
    "preview": "\nmodule.exports = {};"
  },
  {
    "path": "examples/webpack_encore/src/app.js",
    "chars": 48,
    "preview": "console.log('hello world');\nimport './app.scss';"
  },
  {
    "path": "examples/webpack_encore/src/app.scss",
    "chars": 271,
    "preview": ".foo {\n    color: red;\n}\n@media screen and (min-width: 60em) {\n    .foo {\n        color: green;\n    }\n}\n.bar {\n    font-"
  },
  {
    "path": "examples/webpack_encore/webpack.config.js",
    "chars": 937,
    "preview": "const Encore = require('@symfony/webpack-encore');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\ncons"
  },
  {
    "path": "package.json",
    "chars": 898,
    "preview": "{\n  \"name\": \"media-query-plugin\",\n  \"version\": \"1.5.0\",\n  \"description\": \"Webpack plugin for media query extraction.\",\n "
  },
  {
    "path": "src/index.js",
    "chars": 301,
    "preview": "/**\n * Bootstraper that exports the plugin and provides easy access to the loader.\n */\n\nconst loader = require('./loader"
  },
  {
    "path": "src/loader.js",
    "chars": 1630,
    "preview": "/**\n * The loader component is supposed to extract the media CSS from the source chunks.\n * To do this it uses a custom "
  },
  {
    "path": "src/plugin.js",
    "chars": 12238,
    "preview": "/**\n * The plugin component is supposed to inject the extracted media CSS (from store) into the file(s).\n */\n\nconst plug"
  },
  {
    "path": "src/postcss.js",
    "chars": 1850,
    "preview": "/**\n * The PostCSS plugin component is supposed to extract the media CSS from the source chunks.\n * The CSS get saved in"
  },
  {
    "path": "src/store.js",
    "chars": 1548,
    "preview": "/**\n * The store component is supposed to transfer the extracted CSS from the loader to the plugin.\n * Besides it also p"
  },
  {
    "path": "src/utils/escape.js",
    "chars": 588,
    "preview": "// Utility to escape a string.\n// Taken over from css-loader\n// https://github.com/webpack-contrib/css-loader/blob/maste"
  },
  {
    "path": "src/utils/normalize.js",
    "chars": 243,
    "preview": "// Utility to normalize a query for more tolerant comparison.\n// normalize('@media (min-width: 1000px)') === normalize('"
  },
  {
    "path": "test/configs/index.js",
    "chars": 236,
    "preview": "\nmodule.exports = {\n    'only-javascript-output': require('./webpack/only-javascript-output'),\n    'external-css-output'"
  },
  {
    "path": "test/configs/webpack/base.js",
    "chars": 172,
    "preview": "\nmodule.exports = {\n    mode: 'development',\n    devtool: false,\n    entry: {\n        example: './test/data/example.js'\n"
  },
  {
    "path": "test/configs/webpack/external-css-output.js",
    "chars": 1018,
    "preview": "\nconst path = require('path');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst MediaQueryPlugin ="
  },
  {
    "path": "test/configs/webpack/groups-option-output.js",
    "chars": 1073,
    "preview": "\nconst path = require('path');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst MediaQueryPlugin ="
  },
  {
    "path": "test/configs/webpack/only-javascript-output.js",
    "chars": 861,
    "preview": "\nconst path = require('path');\nconst MediaQueryPlugin = require('../../../src');\nconst { merge } = require('webpack-merg"
  },
  {
    "path": "test/data/app.js",
    "chars": 77,
    "preview": "\n// testing groups option\nimport './exampleA.scss';\nimport './exampleB.scss';"
  },
  {
    "path": "test/data/example-desktop.scss",
    "chars": 10,
    "preview": "// wrapper"
  },
  {
    "path": "test/data/example.js",
    "chars": 140,
    "preview": "\nimport './example.scss';\n\nif (window.innerWidth >= 960) {\n    import(/* webpackChunkName: 'example-desktop' */ './examp"
  },
  {
    "path": "test/data/example.scss",
    "chars": 140,
    "preview": ".foo {\n    color: red;\n}\n@media print, screen and (max-width: 60em) {\n    .foo {\n        color: green;\n    }\n}\n.bar {\n  "
  },
  {
    "path": "test/data/exampleA.scss",
    "chars": 110,
    "preview": ".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",
    "chars": 118,
    "preview": ".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",
    "chars": 2605,
    "preview": "\nconst assert = require('chai').assert;\nconst rimraf = require('rimraf');\nconst webpack = require('webpack');\nconst conf"
  }
]

About this extraction

This page contains the full source code of the SassNinja/media-query-plugin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (41.5 KB), approximately 10.1k tokens, and a symbol index with 14 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!