Skip to content
This repository has been archived by the owner on Jul 9, 2018. It is now read-only.

Package: Add @wordpress/custom-templated-path-webpack-plugin package #93

Merged
merged 1 commit into from
Mar 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions packages/custom-templated-path-webpack-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Custom Templated Path Webpack Plugin

Webpack plugin for creating custom path template tags. Extend the [default set of template tags](https://webpack.js.org/configuration/output/#output-filename) with your own custom behavior. Hooks into Webpack's compilation process to allow you to replace tags with a substitute value.

**Note:** This plugin targets Webpack 4.0 and newer, and is not compatible with older versions.

## Usage

Construct an instance of `CustomTemplatedPathPlugin` in your Webpack configurations `plugins` entry, passing an object where keys correspond to the template tag name. The value for each key is a function passed the original intended path and data corresponding to the asset.

The following example creates a new `basename` tag to substitute the basename of each entry file in the build output file. When compiled, the built file will be output as `build-entry.js`.

```js
const { basename } = require( 'path' );
const CustomTemplatedPathPlugin = require( '@wordpress/custom-templated-path-webpack-plugin' );

module.exports = {
// ...

entry: './entry',

output: {
filename: 'build-[basename].js',
},

plugins: [
new CustomTemplatedPathPlugin( {
basename( path, data ) {
let rawRequest;

const entryModule = get( data, [ 'chunk', 'entryModule' ], {} );
switch ( entryModule.type ) {
case 'javascript/auto':
rawRequest = entryModule.rawRequest;
break;

case 'javascript/esm':
rawRequest = entryModule.rootModule.rawRequest;
break;
}

if ( rawRequest ) {
return basename( rawRequest );
}

return path;
},
} ),
],
};
```

For more examples, refer to Webpack's own [`TemplatedPathPlugin.js`](https://github.com/webpack/webpack/blob/v4.1.1/lib/TemplatedPathPlugin.js), which implements the base set of template tags.
33 changes: 33 additions & 0 deletions packages/custom-templated-path-webpack-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@wordpress/custom-templated-path-webpack-plugin",
"version": "1.0.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This initial version here needs to not be 1.0.0, @gziolo has used "version": "1.0.1-0", in the past which has worked fine though I think we should use 1.0.0-alpha.

This all because Lerna bumps this version upon release, if it were to stay at 1.0.0 then Lerna would only be able to publish a patch 1.0.1, minor 1.1.0, or major 2.0.0 release and a 1.0.0 version would never be published.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's annoying 😄 Maybe it'd be fine to publish an initial release manually, without Lerna?

"description": "Webpack plugin for creating custom path template tags",
"author": "WordPress",
"license": "GPL-2.0-or-later",
"keywords": [
"webpack",
"webpack-plugin"
],
"homepage": "https://github.com/WordPress/packages/tree/master/packages/custom-templated-path-webpack-plugin/",
"repository": {
"type": "git",
"url": "https://github.com/WordPress/packages.git"
},
"bugs": {
"url": "https://github.com/WordPress/packages/issues"
},
"main": "build/index.js",
"module": "build-module/index.js",
"publishConfig": {
"access": "public"
},
"dependencies": {
"escape-string-regexp": "^1.0.5"
},
"devDependencies": {
"webpack": "^4.1.1"
},
"peerDependencies": {
"webpack": "^4.0.0"
}
}
52 changes: 52 additions & 0 deletions packages/custom-templated-path-webpack-plugin/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* External dependencies
*/
const escapeStringRegexp = require( 'escape-string-regexp' );

/**
* Webpack plugin for handling specific template tags in Webpack configuration
* values like those supported in the base Webpack functionality (e.g. `name`).
*
* @see webpack.TemplatedPathPlugin
*/
class CustomTemplatedPathPlugin {
/**
* CustomTemplatedPathPlugin constructor. Initializes handlers as a tuple
* set of RegExp, handler, where the regular expression is used in matching
* a Webpack asset path.
*
* @param {Object.<string,Function>} handlers Object keyed by tag to match,
* with function value returning
* replacement string.
*/
constructor( handlers ) {
this.handlers = [];

for ( const [ key, handler ] of Object.entries( handlers ) ) {
const regexp = new RegExp( `\\[${ escapeStringRegexp( key ) }\\]`, 'gi' );
this.handlers.push( [ regexp, handler ] );
}
}

/**
* Webpack plugin application logic.
*
* @param {Object} compiler Webpack compiler
*/
apply( compiler ) {
compiler.hooks.compilation.tap( 'CustomTemplatedPathPlugin', ( compilation ) => {
compilation.mainTemplate.hooks.assetPath.tap( 'CustomTemplatedPathPlugin', ( path, data ) => {
for ( let i = 0; i < this.handlers.length; i++ ) {
const [ regexp, handler ] = this.handlers[ i ];
if ( regexp.test( path ) ) {
return path.replace( regexp, handler( path, data ) );
}
}

return path;
} );
} );
}
}

module.exports = CustomTemplatedPathPlugin;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = null;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* External dependencies
*/

const { basename } = require( 'path' );

/**
* Internal dependencies
*/

const CustomTemplatedPathPlugin = require( '../../' );

module.exports = {
mode: 'development',
context: __dirname,
entry: './entry',
output: {
filename: '[basename].js',
path: __dirname,
},
plugins: [
new CustomTemplatedPathPlugin( {
basename( path, data ) {
console.log(data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it wasn’t expected to be landed 😃

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😞

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least it's in a test file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to add this to jest-console 😎


let rawRequest;
if ( data && data.chunk && data.chunk.entryModule ) {
rawRequest = data.chunk.entryModule.rawRequest;
}

if ( rawRequest ) {
return basename( rawRequest );
}

return path;
},
} ),
],
};
34 changes: 34 additions & 0 deletions packages/custom-templated-path-webpack-plugin/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* External dependencies
*/

const path = require( 'path' );
const { promisify } = require( 'util' );
const webpack = promisify( require( 'webpack' ) );
const fs = require( 'fs' );
const access = promisify( fs.access );
const unlink = promisify( fs.unlink );

/**
* Internal dependencies
*/

const config = require( './fixtures/webpack.config.js' );
const CustomTemplatedPathPlugin = require( '../' );

describe( 'CustomTemplatedPathPlugin', () => {
const outputFile = path.join( __dirname, '/fixtures/entry.js' )

beforeAll( async () => {
// Remove output file so as not to report false positive from previous
// test. Absorb error since the file may not exist (unlink will throw).
try {
await unlink( outputFile );
} catch ( error ) {}
} );

it( 'should resolve with basename output', async () => {
const stats = await webpack( config );
await access( outputFile );
} );
} );