Skip to content
This repository has been archived by the owner on Mar 17, 2021. It is now read-only.

Commit

Permalink
feat: postTransformPublicPath option (#334)
Browse files Browse the repository at this point in the history
  • Loading branch information
fabb authored and evilebottnawi committed Aug 6, 2019
1 parent d441daa commit c136f44
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 0 deletions.
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,32 @@ module.exports = {
};
```

### `postTransformPublicPath`

Type: `Function`
Default: `undefined`

Specifies a custom function to post-process the generated public path. This can be used to prepend or append dynamic global variables that are only available at runtime, like `__webpack_public_path__`. This would not be possible with just `publicPath`, since it stringifies the values.

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
loader: 'file-loader',
options: {
publicPath: '/some/path/',
postTransformPublicPath: (p) => `__webpack_public_path__ + ${p}`,
},
},
],
},
};
```

### `context`

Type: `String`
Expand Down Expand Up @@ -577,6 +603,74 @@ Result:
path/to/file.png?e43b20c069c4a01867c31e98cbce33c9
```

### Dynamic public path depending on environment variable at run time

An application might want to configure different CDN hosts depending on an environment variable that is only available when running the application. This can be an advantage, as only one build of the application is necessary, which behaves differntly depending on environment variables of the deployment environment. Since file-loader is applied when compiling the application, and not when running it, the environment variable cannot be used in the file-loader configuration. A way around this is setting the `__webpack_public_path__` to the desired CDN host depending on the environment variable at the entrypoint of the application. The option `postTransformPublicPath` can be used to configure a custom path depending on a variable like `__webpack_public_path__`.

**main.js**

```js
const namespace = process.env.NAMESPACE;
const assetPrefixForNamespace = (namespace) => {
switch (namespace) {
case 'prod':
return 'https://cache.myserver.net/web';
case 'uat':
return 'https://cache-uat.myserver.net/web';
case 'st':
return 'https://cache-st.myserver.net/web';
case 'dev':
return 'https://cache-dev.myserver.net/web';
default:
return '';
}
};
__webpack_public_path__ = `${assetPrefixForNamespace(namespace)}/`;
```

**file.js**

```js
import png from './image.png';
```

**webpack.config.js**

```js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
loader: 'file-loader',
options: {
context: '',
emitFile: true,
name: '[name].[hash].[ext]',
publicPath: 'static/assets/',
postTransformPublicPath: (p) => `__webpack_public_path__ + ${p}`,
outputPath: 'static/assets/',
},
},
],
},
};
```

Result when run with `NAMESPACE=prod` env variable:

```bash
# result
https://cache.myserver.net/web/static/assets/image.somehash.png
```

Result when run with `NAMESPACE=dev` env variable:

```bash
# result
https://cache-dev.myserver.net/web/static/assets/image.somehash.png
```

## Contributing

Please take a moment to read our contributing guidelines if you haven't yet done so.
Expand Down
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export default function loader(content) {
publicPath = JSON.stringify(publicPath);
}

if (options.postTransformPublicPath) {
publicPath = options.postTransformPublicPath(publicPath);
}

if (typeof options.emitFile === 'undefined' || options.emitFile) {
this.emitFile(outputPath, content);
}
Expand Down
4 changes: 4 additions & 0 deletions src/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
}
]
},
"postTransformPublicPath": {
"description": "A custom transformation function for post-processing the publicPath (https://github.com/webpack-contrib/file-loader#posttransformpublicpath).",
"instanceof": "Function"
},
"context": {
"description": "A custom file context (https://github.com/webpack-contrib/file-loader#context).",
"type": "string"
Expand Down
82 changes: 82 additions & 0 deletions test/__snapshots__/postTransformPublicPath-option.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`when applied with \`postTransformPublicPath\` option matches snapshot for appending to input parameter value 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = __webpack_public_path__ + \\"9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\" + \\"/test\\";",
}
`;

exports[`when applied with \`postTransformPublicPath\` option matches snapshot for returned input parameter value without modification 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = __webpack_public_path__ + \\"9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\";",
}
`;

exports[`when applied with \`publicPath\` and \`postTransformPublicPath\` option \`{Function}\` value matches snapshot for appending to input parameter value 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = \\"public_path/9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\" + \\"?test=test\\";",
}
`;
exports[`when applied with \`publicPath\` and \`postTransformPublicPath\` option \`{Function}\` value matches snapshot for prefixing with __webpack_public_path__ 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = __webpack_public_path__ + \\"public_path/9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\";",
}
`;
exports[`when applied with \`publicPath\` and \`postTransformPublicPath\` option \`{Function}\` value matches snapshot for prefixing with string 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = \\"path_prefix/\\" + \\"public_path/9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\";",
}
`;
exports[`when applied with \`publicPath\` and \`postTransformPublicPath\` option \`{Function}\` value matches snapshot for returned input parameter value without modification 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = \\"public_path/9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\";",
}
`;
exports[`when applied with \`publicPath\` and \`postTransformPublicPath\` option \`{String}\` value matches snapshot for appending to input parameter value 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = \\"public_path/9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\" + \\"?test=test\\";",
}
`;
exports[`when applied with \`publicPath\` and \`postTransformPublicPath\` option \`{String}\` value matches snapshot for prefixing with __webpack_public_path__ 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = __webpack_public_path__ + \\"public_path/9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\";",
}
`;
exports[`when applied with \`publicPath\` and \`postTransformPublicPath\` option \`{String}\` value matches snapshot for returned input parameter value without modification 1`] = `
Object {
"assets": Array [
"9c87cbf3ba33126ffd25ae7f2f6bbafb.png",
],
"source": "module.exports = \\"public_path/9c87cbf3ba33126ffd25ae7f2f6bbafb.png\\";",
}
`;
173 changes: 173 additions & 0 deletions test/postTransformPublicPath-option.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import webpack from './helpers/compiler';

describe('when applied with `postTransformPublicPath` option', () => {
it('matches snapshot for returned input parameter value without modification', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
postTransformPublicPath: (p) => p,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});

it('matches snapshot for appending to input parameter value', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
postTransformPublicPath: (p) => `${p} + "/test"`,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});
});

describe('when applied with `publicPath` and `postTransformPublicPath` option', () => {
describe('`{String}` value', () => {
it('matches snapshot for returned input parameter value without modification', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
publicPath: 'public_path/',
postTransformPublicPath: (p) => p,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});

it('matches snapshot for appending to input parameter value', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
publicPath: 'public_path/',
postTransformPublicPath: (p) => `${p} + "?test=test"`,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});

it('matches snapshot for prefixing with __webpack_public_path__', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
publicPath: 'public_path/',
postTransformPublicPath: (p) => `__webpack_public_path__ + ${p}`,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});
});

describe('`{Function}` value', () => {
it('matches snapshot for returned input parameter value without modification', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
publicPath(url) {
return `public_path/${url}`;
},
postTransformPublicPath: (p) => p,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});

it('matches snapshot for appending to input parameter value', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
publicPath(url) {
return `public_path/${url}`;
},
postTransformPublicPath: (p) => `${p} + "?test=test"`,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});

it('matches snapshot for prefixing with string', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
publicPath: 'public_path/',
postTransformPublicPath: (p) => `"path_prefix/" + ${p}`,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});

it('matches snapshot for prefixing with __webpack_public_path__', async () => {
const config = {
loader: {
test: /(png|jpg|svg)/,
options: {
publicPath: 'public_path/',
postTransformPublicPath: (p) => `__webpack_public_path__ + ${p}`,
},
},
};

const stats = await webpack('fixture.js', config);
const [module] = stats.toJson().modules;
const { assets, source } = module;

expect({ assets, source }).toMatchSnapshot();
});
});
});

0 comments on commit c136f44

Please sign in to comment.