0.2.0 - Mid migration

This commit is contained in:
Daniel Mason 2022-04-25 14:47:15 +12:00
parent 139e6a915e
commit 7e38fdbd7d
42393 changed files with 5358157 additions and 62 deletions

View file

@ -0,0 +1,25 @@
{
"presets": [
[
"env",
{
"useBuiltIns": true,
"targets": {
"node": "current"
},
"exclude": [
"transform-async-to-generator",
"transform-regenerator"
]
}
]
],
"plugins": [
[
"transform-object-rest-spread",
{
"useBuiltIns": true
}
]
]
}

View file

@ -0,0 +1,16 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[Makefile]
indent_style = tab
[*.md]
indent_size = 4
trim_trailing_whitespace = false

View file

@ -0,0 +1 @@
v14.9.0

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Nuno Rodrigues
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.

View file

@ -0,0 +1,61 @@
# Optimize CSS Assets Webpack Plugin
A Webpack plugin to optimize \ minimize CSS assets.
## What does the plugin do?
It will search for CSS assets during the Webpack build and will optimize \ minimize the CSS (by default it uses [cssnano](http://github.com/ben-eb/cssnano) but a custom CSS processor can be specified).
### Solves [extract-text-webpack-plugin](http://github.com/webpack/extract-text-webpack-plugin) CSS duplication problem:
Since [extract-text-webpack-plugin](http://github.com/webpack/extract-text-webpack-plugin) only bundles (merges) text chunks, if it's used to bundle CSS, the bundle might have duplicate entries (chunks can be duplicate free but when merged, duplicate CSS can be created).
## Installation:
Using npm:
```shell
$ npm install --save-dev optimize-css-assets-webpack-plugin
```
> :warning: For webpack v3 or below please use `optimize-css-assets-webpack-plugin@3.2.0`. The `optimize-css-assets-webpack-plugin@4.0.0` version and above supports webpack v4.
## Configuration:
The plugin can receive the following options (all of them are optional):
* `assetNameRegExp`: A regular expression that indicates the names of the assets that should be optimized \ minimized. The regular expression provided is run against the filenames of the files exported by the `ExtractTextPlugin` instances in your configuration, not the filenames of your source CSS files. Defaults to `/\.css$/g`
* `cssProcessor`: The CSS processor used to optimize \ minimize the CSS, defaults to [`cssnano`](http://github.com/ben-eb/cssnano). This should be a function that follows `cssnano.process` interface (receives a CSS and options parameters and returns a Promise).
* `cssProcessorOptions`: The options passed to the `cssProcessor`, defaults to `{}`
* `cssProcessorPluginOptions`: The plugin options passed to the `cssProcessor`, defaults to `{}`
* `canPrint`: A boolean indicating if the plugin can print messages to the console, defaults to `true`
## Example:
``` javascript
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
}
]
},
plugins: [
new ExtractTextPlugin('styles.css'),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.optimize\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
canPrint: true
})
]
};
```
## License
MIT (http://www.opensource.org/licenses/mit-license.php)

View file

@ -0,0 +1,51 @@
{
"name": "optimize-css-assets-webpack-plugin",
"version": "5.0.4",
"description": "A Webpack plugin to optimize \\ minimize CSS assets.",
"keywords": [
"CSS",
"duplicate",
"extract-text-webpack-plugin",
"minimize",
"optimize",
"remove",
"webpack"
],
"homepage": "http://github.com/NMFR/optimize-css-assets-webpack-plugin",
"license": "MIT",
"author": "Nuno Rodrigues",
"main": "src/index.js",
"repository": {
"type": "git",
"url": "http://github.com/NMFR/optimize-css-assets-webpack-plugin.git"
},
"scripts": {
"test": "jest",
"test:watch": "jest --watch"
},
"jest": {
"testEnvironment": "node",
"watchPathIgnorePatterns": [
"<rootDir>/test/js/*.*"
]
},
"dependencies": {
"cssnano": "^4.1.10",
"last-call-webpack-plugin": "^3.0.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-jest": "^26.3.0",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"css-loader": "^3.6.0",
"extract-text-webpack-plugin": "next",
"jest": "^26.4.2",
"style-loader": "^0.20.1",
"webpack": "^4.44.1"
},
"peerDependencies": {
"webpack": "^4.0.0"
}
}

View file

@ -0,0 +1,83 @@
const url = require('url');
const LastCallWebpackPlugin = require('last-call-webpack-plugin');
class OptimizeCssAssetsWebpackPlugin extends LastCallWebpackPlugin {
constructor(options) {
super({
assetProcessors: [
{
phase: LastCallWebpackPlugin.PHASES.OPTIMIZE_CHUNK_ASSETS,
regExp: (options && options.assetNameRegExp) || /\.css(\?.*)?$/i,
processor: (assetName, asset, assets) =>
this.processCss(assetName, asset, assets),
}
],
canPrint: options && options.canPrint,
});
this.options.assetNameRegExp = !options || !options.assetNameRegExp ?
/\.css(\?.*)?$/i :
options.assetNameRegExp;
this.options.cssProcessor = !options || !options.cssProcessor ?
require('cssnano') :
options.cssProcessor;
this.options.cssProcessorOptions = !options || options.cssProcessorOptions === undefined ?
{} :
options.cssProcessorOptions;
this.options.cssProcessorPluginOptions = !options || options.cssProcessorPluginOptions === undefined ?
{} :
options.cssProcessorPluginOptions;
}
buildPluginDescriptor() {
return { name: 'OptimizeCssAssetsWebpackPlugin' };
}
processCss(assetName, asset, assets) {
const parse = url.parse(assetName);
const assetInfo = {
path: parse.pathname,
query: parse.query ? `?${parse.query}` : '',
};
const css = asset.sourceAndMap ? asset.sourceAndMap() : { source: asset.source() };
const processOptions = Object.assign(
{ from: assetName, to: assetName },
this.options.cssProcessorOptions
);
if (processOptions.map && !processOptions.map.prev) {
try {
let map = css.map;
if (!map) {
const mapJson = assets.getAsset(`${assetInfo.path}.map`);
if (mapJson) {
map = JSON.parse(mapJson);
}
}
if (
map &&
(
(map.sources && map.sources.length > 0) ||
(map.mappings && map.mappings.length > 0)
)
) {
processOptions.map = Object.assign({ prev: map }, processOptions.map);
}
} catch (err) {
console.warn('OptimizeCssAssetsPlugin.processCss() Error getting previous source map', err);
}
}
return this.options
.cssProcessor.process(css.source, processOptions, this.options.cssProcessorPluginOptions)
.then(r => {
if (processOptions.map && r.map && r.map.toString) {
assets.setAsset(`${assetInfo.path}.map${assetInfo.query}`, r.map.toString());
}
return r.css;
});
}
}
module.exports = OptimizeCssAssetsWebpackPlugin;

View file

@ -0,0 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Webpack Integration Tests assetNameRegExp-no-source 1`] = `"body{color:red}a{color:blue}body{margin:0;color:red}p{margin:1000px}body{color:red;padding:0;margin:0}p{padding:500px;padding:1000px}"`;
exports[`Webpack Integration Tests duplicate-css-exists-without-plugin 1`] = `"body{color:red}a{color:blue}body{color:red}p{color:green}"`;
exports[`Webpack Integration Tests only-assetNameRegExp-processed 1`] = `
"body {
color: red;
padding: 0;
margin: 0;
}
p {
padding: 500px;
padding: 1000px;
}
"
`;
exports[`Webpack Integration Tests only-assetNameRegExp-processed 2`] = `"a{color:#00f}body{margin:0;color:red}p{margin:1000px}"`;
exports[`Webpack Integration Tests removes-duplicate-css 1`] = `"a{color:#00f}body{color:red}p{color:green}"`;

View file

@ -0,0 +1 @@
body{color:red}a{color:blue}

View file

@ -0,0 +1 @@
body{margin:0;color:red}p{margin:1000px}

View file

@ -0,0 +1 @@
body{color:red;padding:0;margin:0}p{padding:500px;padding:1000px}

View file

@ -0,0 +1 @@
body{color:red}a{color:blue}body{margin:0;color:red}p{margin:1000px}body{color:red;padding:0;margin:0}p{padding:500px;padding:1000px}

View file

@ -0,0 +1,10 @@
/*
This test is here to confirm that assetNameRegExp option will apply
only to the names of the files exported byt ExtractTextPlugin
*/
require('./a_optimize-me.css');
require('./b_optimize-me.css');
require('./c.css');

View file

@ -0,0 +1,25 @@
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import OptimizeCssAssetsPlugin from '../../../src/';
module.exports = {
entry: './index',
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: { loader: 'style-loader' },
use: {
loader: 'css-loader'
}
})
},
],
},
plugins: [
new ExtractTextPlugin('file.css'),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /optimize-me\.css/g
})
],
};

View file

@ -0,0 +1 @@
body{color:red}a{color:blue}

View file

@ -0,0 +1 @@
body{color:red}p{color:green}

View file

@ -0,0 +1 @@
body{color:red}a{color:blue}body{color:red}p{color:green}

View file

@ -0,0 +1,2 @@
require('./a.css');
require('./b.css');

View file

@ -0,0 +1,20 @@
import ExtractTextPlugin from "extract-text-webpack-plugin";
import OptimizeCssAssetsPlugin from "../../../src/";
module.exports = {
entry: "./index",
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: { loader: "style-loader" },
use: {
loader: "css-loader"
}
})
}
]
},
plugins: [new ExtractTextPlugin("file.css")]
};

View file

@ -0,0 +1,6 @@
body {
color: red;
}
a {
color: blue;
}

View file

@ -0,0 +1,7 @@
body {
margin: 0;
color: red;
}
p {
margin: 1000px;
}

View file

@ -0,0 +1,9 @@
body {
color: red;
padding: 0;
margin: 0;
}
p {
padding: 500px;
padding: 1000px;
}

View file

@ -0,0 +1,9 @@
body {
color: red;
padding: 0;
margin: 0;
}
p {
padding: 500px;
padding: 1000px;
}

View file

@ -0,0 +1 @@
a{color:#00f}body{margin:0;color:red}p{margin:1000px}

View file

@ -0,0 +1,3 @@
require('./a_optimize-me.css');
require('./b_optimize-me.css');
require('./c_as-is.css');

View file

@ -0,0 +1,38 @@
import ExtractTextPlugin from "extract-text-webpack-plugin";
import OptimizeCssAssetsPlugin from "../../../src/";
const notToProcess = new ExtractTextPlugin("as_is.css");
const toProcess = new ExtractTextPlugin("optimize.css");
module.exports = {
entry: "./index",
module: {
rules: [
{
test: /as-is\.css$/,
use: notToProcess.extract({
fallback: { loader: "style-loader" },
use: {
loader: "css-loader"
}
})
},
{
test: /optimize-me\.css$/,
use: toProcess.extract({
fallback: { loader: "style-loader" },
use: {
loader: "css-loader"
}
})
}
]
},
plugins: [
notToProcess,
toProcess,
new OptimizeCssAssetsPlugin({
assetNameRegExp: /optimize\.css/g
})
]
};

View file

@ -0,0 +1,6 @@
body {
color: red;
}
a {
color: blue;
}

View file

@ -0,0 +1,6 @@
body {
color: red;
}
p {
color: green;
}

View file

@ -0,0 +1 @@
a{color:#00f}body{color:red}p{color:green}

View file

@ -0,0 +1,2 @@
require('./a.css');
require('./b.css');

View file

@ -0,0 +1,20 @@
import ExtractTextPlugin from "extract-text-webpack-plugin";
import OptimizeCssAssetsPlugin from "../../../src/";
module.exports = {
entry: "./index",
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: { loader: "style-loader" },
use: {
loader: "css-loader"
}
})
}
]
},
plugins: [new ExtractTextPlugin("file.css"), new OptimizeCssAssetsPlugin()]
};

View file

@ -0,0 +1,26 @@
import OptimizeCssAssetsPlugin from '../src/';
describe('plugin test', () => {
it('does not throw when called', () => {
expect(() => {
new OptimizeCssAssetsPlugin();
}).not.toThrow();
});
it('can override default parameters', () => {
const assetNameRegExp = /\.optimize\.css$/
const cssProcessor = {};
const cssProcessorOptions = { discardComments: { removeAll: true } };
const canPrint = false;
const plugin = new OptimizeCssAssetsPlugin({
assetNameRegExp,
cssProcessor,
cssProcessorOptions,
canPrint
});
expect(plugin.options.assetNameRegExp).toEqual(assetNameRegExp);
expect(plugin.options.cssProcessor).toEqual(cssProcessor);
expect(plugin.options.cssProcessorOptions).toEqual(cssProcessorOptions);
expect(plugin.options.canPrint).toEqual(canPrint);
});
});

View file

@ -0,0 +1 @@
html{display:none}

View file

@ -0,0 +1,39 @@
import fs from "fs";
import path from "path";
import ExtractTextPlugin from "extract-text-webpack-plugin";
export function readFileOrEmpty(path) {
try {
return fs.readFileSync(path, "utf-8");
} catch (e) {
return "";
}
}
export const defaultConfig = {
entry: "./index",
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: { loader: "style-loader" },
use: {
loader: "css-loader"
}
})
}
]
},
plugins: [],
context: __dirname,
output: {
filename: "destination.js",
path: path.resolve(__dirname, "../", "js", "default-exports")
}
};
export function checkForWebpackErrors({ err, stats, done }) {
if (err) return done(err);
if (stats.hasErrors()) return done(new Error(stats.toString()));
}

View file

@ -0,0 +1 @@
require('./default.css');

View file

@ -0,0 +1,82 @@
/* eslint-disable import/no-dynamic-require, global-require */
import fs from 'fs';
import path from 'path';
import webpack from 'webpack';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import OptimizeCssAssetsPlugin from '../src/';
import { readFileOrEmpty, defaultConfig, checkForWebpackErrors } from './util/helpers';
const cases = process.env.CASES ? process.env.CASES.split(',') : fs.readdirSync(path.join(__dirname, 'cases'));
describe('Webpack Integration Tests', () => {
cases.forEach((testCase) => {
if (/^_skip_/.test(testCase)) return;
it(testCase, (done) => {
const testDirectory = path.join(__dirname, 'cases', testCase);
const outputDirectory = path.join(__dirname, 'js', testCase);
const expectedDirectory = path.join(testDirectory, 'expected');
const configFile = path.join(testDirectory, 'webpack.config.js');
const config = Object.assign(
fs.existsSync(configFile) ? require(configFile) : { entry: { test: './index.js' } },
{
context: testDirectory,
output: {
filename: '[name].js',
path: outputDirectory
}
}
);
webpack(config, (err, stats) => {
checkForWebpackErrors({ err, stats, done });
fs.readdirSync(expectedDirectory).forEach((file) => {
const expectedFile = readFileOrEmpty(path.join(expectedDirectory, file));
const actualFile = readFileOrEmpty(path.join(outputDirectory, file));
expect(actualFile).toEqual(expectedFile);
expect(actualFile).toMatchSnapshot();
});
done();
});
});
});
it('calls cssProcessor with correct arguments', (done) => {
const destination = 'destination.css';
const expectedCss = readFileOrEmpty(__dirname + '/util/default.css');
const cssProcessorOptions = { discardComments: { removeAll: true } };
const cssProcessor = {
process: (actualCss, options) => {
expect(options).toEqual(expect.objectContaining(cssProcessorOptions));
expect(actualCss).toEqual(expectedCss);
return Promise.resolve({ css: actualCss });
}
};
const plugin = new OptimizeCssAssetsPlugin({ cssProcessor, cssProcessorOptions });
const config = Object.assign(defaultConfig, {plugins: [plugin, new ExtractTextPlugin(destination)]});
webpack(config, (err, stats) => {
checkForWebpackErrors({ err, stats, done });
done();
});
});
it('writes processed css to destination', (done) => {
const destination = 'destination.css';
const expectedCss = '.inifinity-pool{overflow:hidden;}';
const fakeCssProcessor = {
process: jest.fn().mockReturnValue(Promise.resolve({ css: expectedCss }))
};
const plugin = new OptimizeCssAssetsPlugin({ cssProcessor: fakeCssProcessor });
const config = Object.assign(defaultConfig, {plugins: [plugin, new ExtractTextPlugin(destination)]});
webpack(config, (err, stats) => {
checkForWebpackErrors({ err, stats, done });
const actualCss = readFileOrEmpty(__dirname + '/js/default-exports/destination.css');
expect(fakeCssProcessor.process).toHaveBeenCalled();
expect(actualCss).toEqual(expectedCss);
done();
});
});
});