Skip to content

Commit b92fd4b

Browse files
hsablonniereLarsDenBakker
authored andcommitted
feat(rollup-plugin-import-meta-assets): init plugin
1 parent 2393027 commit b92fd4b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+741
-1
lines changed

.eslintignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
/packages/test-runner-mocha/*.d.ts
55
/packages/config-loader/
66
/packages/*/test/**/fixtures
7+
/packages/*/test/**/snapshots
78
/packages/dev-server-rollup/test/browser/**/*
89
node_modules
910
dist
1011
demo
1112
CHANGELOG.md
1213
.changeset
13-
_site
14+
_site

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.js eol=lf
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
---
2+
title: Rollup Plugin import-meta-assets
3+
eleventyNavigation:
4+
key: Rollup Plugin import-meta-assets
5+
parent: Building
6+
order: 2
7+
---
8+
9+
Rollup plugin that detects assets references relative to modules using patterns such as `new URL('./path/to/asset.ext', import.meta.url)`. The assets are added to the rollup pipeline, allowing them to be transformed and hash the filenames.
10+
11+
## Install
12+
13+
Using npm:
14+
15+
```
16+
npm install @web/rollup-plugin-import-meta-assets --save-dev
17+
```
18+
19+
## Usage
20+
21+
Create a rollup.config.js [configuration file](https://www.rollupjs.org/guide/en/#configuration-files) and import the plugin:
22+
23+
```js
24+
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
25+
26+
export default {
27+
input: 'src/index.js',
28+
output: {
29+
dir: 'output',
30+
format: 'es',
31+
},
32+
plugins: [importMetaAssets()],
33+
};
34+
```
35+
36+
Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#command-line-reference) or the [API](https://www.rollupjs.org/guide/en/#javascript-api).
37+
38+
## Options
39+
40+
### `exclude`
41+
42+
Type: `String` | `Array[...String]`<br>
43+
Default: `null`
44+
45+
A [picomatch pattern](https://github.com/micromatch/picomatch#globbing-features), or array of patterns, which specifies the files in the build the plugin should _ignore_.
46+
By default no files are ignored.
47+
48+
### `include`
49+
50+
Type: `String` | `Array[...String]`<br>
51+
Default: `null`
52+
53+
A [picomatch pattern](https://github.com/micromatch/picomatch#globbing-features), or array of patterns, which specifies the files in the build the plugin should operate on.
54+
By default all files are targeted.
55+
56+
### `warnOnError`
57+
58+
Type: `Boolean`<br>
59+
Default: `false`
60+
61+
By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched.
62+
63+
### `transform`
64+
65+
Type: `Function`<br>
66+
Default: `null`
67+
68+
By default, referenced assets detected by this plugin are just copied as is to the output directory, according to your configuration.
69+
70+
When `transform` is defined, this function will be called for each asset with two parameters, the content of the asset as a [Buffer](https://nodejs.org/api/buffer.html) and the absolute path.
71+
This allows you to conditionnaly match on the absolute path and maybe transform the content.
72+
73+
In this example, we use it to optimize SVG images with [svgo](https://github.com/svg/svgo):
74+
75+
```js
76+
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
77+
const svgo = new SVGO({
78+
// See https://github.com/svg/svgo#what-it-can-do
79+
plugins: [ /* plugins here */],
80+
});
81+
82+
export default {
83+
input: 'src/index.js',
84+
output: {
85+
dir: 'output',
86+
format: 'es',
87+
},
88+
plugins: [
89+
importMetaAssets({
90+
transform: (assetBuffer, assetPath) => {
91+
return assetPath.endsWith('.svg')
92+
? svgo.optimize(assetBuffer.toString()).then(({ data }) => data);
93+
: assetBuffer;
94+
},
95+
}),
96+
],
97+
};
98+
```
99+
100+
## Examples
101+
102+
Source directory:
103+
104+
```
105+
.
106+
├── one
107+
│ └── two
108+
│ └── the-image.svg
109+
├──
110+
└── entrypoint.js
111+
```
112+
113+
With `entrypoint.js` containing this:
114+
115+
```js
116+
const imageUrl = new URL('./one/two/the-image.svg', import.meta.url).href;
117+
console.log(imageUrl);
118+
```
119+
120+
Output directory:
121+
122+
```
123+
.
124+
├── assets
125+
│ └── the-image.svg
126+
└── bundle.js
127+
```
128+
129+
With `bundle.js` containing this:
130+
131+
```js
132+
const imageUrl = new URL(new URL('asset/the-image.svg', import.meta.url).href, import.meta.url)
133+
.href;
134+
console.log(imageUrl);
135+
```

packages/rollup-plugin-import-meta-assets/CHANGELOG.md

Whitespace-only changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Rollup Plugin import-meta-assets
2+
3+
Rollup plugin that detects assets references relative to modules using patterns such as `new URL('./path/to/asset.ext', import.meta.url)`. The assets are added to the rollup pipeline, allowing them to be transformed and hash the filenames.
4+
5+
## Install
6+
7+
Using npm:
8+
9+
```
10+
npm install @web/rollup-plugin-import-meta-assets --save-dev
11+
```
12+
13+
## Usage
14+
15+
Create a rollup.config.js [configuration file](https://www.rollupjs.org/guide/en/#configuration-files) and import the plugin:
16+
17+
```js
18+
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
19+
20+
export default {
21+
input: 'src/index.js',
22+
output: {
23+
dir: 'output',
24+
format: 'es',
25+
},
26+
plugins: [importMetaAssets()],
27+
};
28+
```
29+
30+
Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#command-line-reference) or the [API](https://www.rollupjs.org/guide/en/#javascript-api).
31+
32+
## Documentation
33+
34+
See [our website](https://modern-web.dev/docs/building/rollup-plugin-import-meta-assets/) for full documentation.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import cjsEntrypoint from './src/rollup-plugin-import-meta-assets.js';
2+
3+
const { importMetaAssets } = cjsEntrypoint;
4+
5+
export { importMetaAssets };
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@web/rollup-plugin-import-meta-assets",
3+
"version": "0.0.0",
4+
"publishConfig": {
5+
"access": "public"
6+
},
7+
"description": "Rollup plugin that detects assets references relative to modules using patterns such as `new URL('./path/to/asset.ext', import.meta.url)`. The assets are added to the rollup pipeline, allowing them to be transformed and hash the filenames.",
8+
"license": "MIT",
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/modernweb-dev/web.git",
12+
"directory": "packages/rollup-plugin-import-meta-assets"
13+
},
14+
"author": "modern-web",
15+
"homepage": "https://github.com/modernweb-dev/web/tree/master/packages/rollup-plugin-import-meta-assets",
16+
"main": "dist/index.js",
17+
"engines": {
18+
"node": ">=10.0.0"
19+
},
20+
"scripts": {
21+
"test": "npm run test:node",
22+
"test:node": "mocha test/**/*.test.js test/*.test.js",
23+
"test:update-snapshots": "mocha test/**/*.test.js test/*.test.js --update-snapshots",
24+
"test:watch": "npm run test:node -- --watch"
25+
},
26+
"files": [
27+
"dist"
28+
],
29+
"keywords": [
30+
"rollup",
31+
"plugin",
32+
"import-meta"
33+
],
34+
"dependencies": {
35+
"@rollup/pluginutils": "^3.1.0",
36+
"estree-walker": "^2.0.1",
37+
"magic-string": "^0.25.7"
38+
}
39+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
'use strict';
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
const { createFilter } = require('@rollup/pluginutils');
6+
const { asyncWalk } = require('estree-walker');
7+
const MagicString = require('magic-string');
8+
9+
/**
10+
* Extract the relative path from an AST node representing this kind of expression `new URL('./path/to/asset.ext', import.meta.url)`.
11+
*
12+
* @param {import('estree').Node} node - The AST node
13+
* @returns {string} The relative path
14+
*/
15+
function getRelativeAssetPath(node) {
16+
const browserPath = node.arguments[0].value;
17+
return browserPath.split('/').join(path.sep);
18+
}
19+
20+
/**
21+
* Checks if a AST node represents this kind of expression: `new URL('./path/to/asset.ext', import.meta.url)`.
22+
*
23+
* @param {import('estree').Node} node - The AST node
24+
* @returns {boolean}
25+
*/
26+
function isNewUrlImportMetaUrl(node) {
27+
return (
28+
node.type === 'NewExpression' &&
29+
node.callee.type === 'Identifier' &&
30+
node.callee.name === 'URL' &&
31+
node.arguments.length === 2 &&
32+
node.arguments[0].type === 'Literal' &&
33+
typeof getRelativeAssetPath(node) === 'string' &&
34+
node.arguments[1].type === 'MemberExpression' &&
35+
node.arguments[1].object.type === 'MetaProperty' &&
36+
node.arguments[1].property.type === 'Identifier' &&
37+
node.arguments[1].property.name === 'url'
38+
);
39+
}
40+
41+
/**
42+
* Detects assets references relative to modules using patterns such as `new URL('./path/to/asset.ext', import.meta.url)`.
43+
* The assets are added to the rollup pipeline, allowing them to be transformed and hash the filenames.
44+
*
45+
* @param {object} options
46+
* @param {string|string[]} [options.include] A picomatch pattern, or array of patterns, which specifies the files in the build the plugin should operate on. By default all files are targeted.
47+
* @param {string|string[]} [options.exclude] A picomatch pattern, or array of patterns, which specifies the files in the build the plugin should _ignore_. By default no files are ignored.
48+
* @param {boolean} [options.warnOnError] By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched.
49+
* @param {function} [options.transform] A function to transform assets.
50+
* @return {import('rollup').Plugin} A Rollup Plugin
51+
*/
52+
function importMetaAssets({ include, exclude, warnOnError, transform } = {}) {
53+
const filter = createFilter(include, exclude);
54+
55+
return {
56+
name: 'rollup-plugin-import-meta-assets',
57+
58+
async transform(code, id) {
59+
if (!filter(id)) {
60+
return null;
61+
}
62+
63+
const ast = this.parse(code);
64+
const magicString = new MagicString(code);
65+
66+
await asyncWalk(ast, {
67+
enter: async node => {
68+
if (isNewUrlImportMetaUrl(node)) {
69+
const absoluteScriptDir = path.dirname(id);
70+
const relativeAssetPath = getRelativeAssetPath(node);
71+
const absoluteAssetPath = path.resolve(absoluteScriptDir, relativeAssetPath);
72+
const assetName = path.basename(absoluteAssetPath);
73+
74+
try {
75+
const assetContents = await fs.promises.readFile(absoluteAssetPath);
76+
const transformedAssetContents =
77+
transform != null
78+
? await transform(assetContents, absoluteAssetPath)
79+
: assetContents;
80+
const ref = this.emitFile({
81+
type: 'asset',
82+
name: assetName,
83+
source: transformedAssetContents,
84+
});
85+
magicString.overwrite(
86+
node.arguments[0].start,
87+
node.arguments[0].end,
88+
`import.meta.ROLLUP_FILE_URL_${ref}`,
89+
);
90+
} catch (error) {
91+
if (warnOnError) {
92+
this.warn(error, node.object.arguments[0].start);
93+
} else {
94+
this.error(error, node.object.arguments[0].start);
95+
}
96+
}
97+
}
98+
},
99+
});
100+
101+
return {
102+
code: magicString.toString(),
103+
map: magicString.generateMap(),
104+
};
105+
},
106+
};
107+
}
108+
109+
module.exports = { importMetaAssets };
Lines changed: 5 additions & 0 deletions
Loading
Loading
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { imageOne, nameOne } from './one/one.js';
2+
import { imageTwo, nameTwo } from './one/two/two.js';
3+
import { imageThree, nameThree } from './one/two/three/three.js';
4+
import { imageFour, nameFour } from './one/two/three/four/four.js';
5+
6+
console.log({
7+
[nameOne]: imageOne,
8+
[nameTwo]: imageTwo,
9+
[nameThree]: imageThree,
10+
[nameFour]: imageFour,
11+
});
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const nameOne = 'one-name';
2+
export const imageOne = new URL('../one.svg', import.meta.url).href;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const nameOne = 'one-name';
2+
const imageOne = new URL('../one-deep.svg', import.meta.url).href;
3+
4+
const nameTwo = 'two-name';
5+
const imageTwo = new URL('./two-deep.svg', import.meta.url).href;
6+
7+
const nameThree = 'three-name';
8+
const imageThree = new URL('./three/three-deep.svg', import.meta.url).href;
9+
10+
const nameFour = 'four-name';
11+
const imageFour = new URL('./three/four/four-deep.svg', import.meta.url).href;
12+
13+
console.log({
14+
[nameOne]: imageOne,
15+
[nameTwo]: imageTwo,
16+
[nameThree]: imageThree,
17+
[nameFour]: imageFour,
18+
});

0 commit comments

Comments
 (0)