From 93339727e5e9757bf2a7f3230789303d171280d7 Mon Sep 17 00:00:00 2001 From: Marcelo Serpa <81248+fullofcaffeine@users.noreply.github.com> Date: Thu, 20 May 2021 21:01:53 -0500 Subject: [PATCH] Add packaged version PoC and tests --- add-readable-js-assets-webpack-plugin.js | 6 ++- docs/manifest.json | 6 +++ .../README.md | 37 +++++++++++++ .../index.js | 41 ++++++++++++++ .../package.json | 36 +++++++++++++ .../test/build.js | 53 +++++++++++++++++++ .../test/fixtures/webpack.config.js | 47 ++++++++++++++++ 7 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 packages/readable-js-assets-webpack-plugin/README.md create mode 100644 packages/readable-js-assets-webpack-plugin/index.js create mode 100644 packages/readable-js-assets-webpack-plugin/package.json create mode 100644 packages/readable-js-assets-webpack-plugin/test/build.js create mode 100644 packages/readable-js-assets-webpack-plugin/test/fixtures/webpack.config.js diff --git a/add-readable-js-assets-webpack-plugin.js b/add-readable-js-assets-webpack-plugin.js index 246146900fc69..1ac0a5f6b1893 100644 --- a/add-readable-js-assets-webpack-plugin.js +++ b/add-readable-js-assets-webpack-plugin.js @@ -2,6 +2,7 @@ * External dependencies */ const fs = require( 'fs' ); +const path = require( 'path' ); class AddReadableJsAssetsWebpackPlugin { extractUnminifiedFiles( compilation ) { @@ -14,7 +15,10 @@ class AddReadableJsAssetsWebpackPlugin { } async writeUnminifiedFiles( compilation ) { for ( const [ file, source ] of compilation.unminifiedAssets ) { - await fs.promises.writeFile( file, source ); + await fs.promises.writeFile( + path.join( compilation.options.output.path, file ), + source + ); } } apply( compiler ) { diff --git a/docs/manifest.json b/docs/manifest.json index b65c0aead24a5..b0a076aeaa310 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1667,6 +1667,12 @@ "markdown_source": "../packages/react-native-editor/README.md", "parent": "packages" }, + { + "title": "@wordpress/readable-js-assets-webpack-plugin", + "slug": "packages-readable-js-assets-webpack-plugin", + "markdown_source": "../packages/readable-js-assets-webpack-plugin/README.md", + "parent": "packages" + }, { "title": "@wordpress/redux-routine", "slug": "packages-redux-routine", diff --git a/packages/readable-js-assets-webpack-plugin/README.md b/packages/readable-js-assets-webpack-plugin/README.md new file mode 100644 index 0000000000000..23d83b8f7cd1a --- /dev/null +++ b/packages/readable-js-assets-webpack-plugin/README.md @@ -0,0 +1,37 @@ +# Readable JS assets WebPack Plugin + +Generate a readable non-minified JS file for each `.min.js` asset. + +The end result is that for each JS entrypoint, we get a set of readable and non-minimized `.js` file and a minimized `.min.js`. This allows Gutenberg to follow the WordPress convention of adding a `.min.js` suffix to minimized JS files, while still providing a readable and unminized files that play well with the WordPress i18n machinery. + +Consult the [webpack website](https://webpack.js.org) for additional information on webpack concepts. + +## Installation + +Install the module + +```bash +npm install @wordpress/readable-js-assets-webpack-plugin --save-dev +``` + +**Note**: This package requires Node.js 12.0.0 or later. It also requires webpack 4.8.3 and newer. It is not compatible with older versions. + +## Usage + +### Webpack + +Use this plugin as you would other webpack plugins: + +```js +// webpack.config.js +const ReadableJsAssetsWebpackPlugin = require( '@wordpress/readable-js-assets-webpack-plugin' ); + +module.exports = { + // …snip + plugins: [ new ReadableJsAssetsWebpackPlugin() ], +}; +``` + +**Note:** +- Multiple instances of the plugin are not supported and may produced unexpected results; +- It assumes your WebPack pipeline is already generating a `.min.js` JS asset file for each JS entry-point. diff --git a/packages/readable-js-assets-webpack-plugin/index.js b/packages/readable-js-assets-webpack-plugin/index.js new file mode 100644 index 0000000000000..1ac0a5f6b1893 --- /dev/null +++ b/packages/readable-js-assets-webpack-plugin/index.js @@ -0,0 +1,41 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ); +const path = require( 'path' ); + +class AddReadableJsAssetsWebpackPlugin { + extractUnminifiedFiles( compilation ) { + const files = compilation.chunks.flatMap( ( chunk ) => chunk.files ); + compilation.unminifiedAssets = files.map( ( file ) => { + const asset = compilation.assets[ file ]; + const unminifiedFile = file.replace( /\.min\.js$/, '.js' ); + return [ unminifiedFile, asset.source() ]; + } ); + } + async writeUnminifiedFiles( compilation ) { + for ( const [ file, source ] of compilation.unminifiedAssets ) { + await fs.promises.writeFile( + path.join( compilation.options.output.path, file ), + source + ); + } + } + apply( compiler ) { + compiler.hooks.compilation.tap( + this.constructor.name, + ( compilation ) => { + compilation.hooks.additionalAssets.tap( + this.constructor.name, + () => this.extractUnminifiedFiles( compilation ) + ); + } + ); + compiler.hooks.afterEmit.tapPromise( + this.constructor.name, + ( compilation ) => this.writeUnminifiedFiles( compilation ) + ); + } +} + +module.exports = AddReadableJsAssetsWebpackPlugin; diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json new file mode 100644 index 0000000000000..c7a63a9f4cc1e --- /dev/null +++ b/packages/readable-js-assets-webpack-plugin/package.json @@ -0,0 +1,36 @@ +{ + "name": "@wordpress/readable-js-assets-webpack-plugin", + "version": "1.0", + "description": "Generate a readable JS file for each JS asset.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "gutenberg", + "webpack", + "readable", + "minimizer" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/packages/readable-js-assets-webpack-plugin/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/readable-js-assets-webpack-plugin" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "engines": { + "node": ">=12.0" + }, + "files": [ + "index.js" + ], + "main": "index.js", + "peerDependencies": { + "webpack": "^4.8.3 || ^5.0.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/readable-js-assets-webpack-plugin/test/build.js b/packages/readable-js-assets-webpack-plugin/test/build.js new file mode 100644 index 0000000000000..7332cf142cc54 --- /dev/null +++ b/packages/readable-js-assets-webpack-plugin/test/build.js @@ -0,0 +1,53 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ); +const glob = require( 'glob' ).sync; +const mkdirp = require( 'mkdirp' ).sync; +const path = require( 'path' ); +const rimraf = require( 'rimraf' ).sync; +const webpack = require( 'webpack' ); + +const testDirectory = path.join( __dirname, 'fixtures' ); + +afterAll( () => rimraf( path.join( __dirname, 'build' ) ) ); + +describe( 'ReadableJsAssetsWebpackPlugin', () => { + const outputDirectory = path.join( __dirname, 'build' ); + + beforeEach( () => { + rimraf( outputDirectory ); + mkdirp( outputDirectory ); + } ); + + // This afterEach is necessary to prevent watched tests from retriggering on every run. + afterEach( () => rimraf( outputDirectory ) ); + + test( 'should produce the expected output', () => + new Promise( ( resolve ) => { + const options = Object.assign( + { + context: testDirectory, + }, + require( path.join( testDirectory, 'webpack.config.js' ) ) + ); + options.output.path = outputDirectory; + + webpack( options, ( err ) => { + expect( err ).toBeNull(); + + const assetFiles = glob( `${ outputDirectory }/*.js` ); + + expect( assetFiles ).toHaveLength( 4 ); + + // Asset files should match. + assetFiles.forEach( ( assetFile ) => { + expect( + fs.readFileSync( assetFile, 'utf-8' ) + ).toMatchSnapshot( 'Asset file should match snapshot' ); + } ); + + resolve(); + } ); + } ) ); +} ); diff --git a/packages/readable-js-assets-webpack-plugin/test/fixtures/webpack.config.js b/packages/readable-js-assets-webpack-plugin/test/fixtures/webpack.config.js new file mode 100644 index 0000000000000..dc3cfd472dc9a --- /dev/null +++ b/packages/readable-js-assets-webpack-plugin/test/fixtures/webpack.config.js @@ -0,0 +1,47 @@ +/** + * External dependencies + */ +const TerserPlugin = require( 'terser-webpack-plugin' ); +const path = require( 'path' ); + +/** + * Internal dependencies + */ +const ReadableJsAssetsWebpackPlugin = require( '../..' ); + +module.exports = { + mode: 'production', + optimization: { + // Only concatenate modules in production, when not analyzing bundles. + chunkIds: 'named', + moduleIds: 'named', + concatenateModules: true, + minimizer: [ + new TerserPlugin( { + cache: true, + parallel: true, + sourceMap: true, + terserOptions: { + output: { + comments: /translators:/i, + }, + compress: { + passes: 2, + }, + mangle: { + reserved: [ '__', '_n', '_nx', '_x' ], + }, + }, + extractComments: false, + } ), + ], + }, + entry: { + index: './index.js', + frontend: './frontend.js', + }, + output: { + filename: '[name].min.js', + }, + plugins: [ new ReadableJsAssetsWebpackPlugin() ], +};