This commit is contained in:
darenhsu
2022-07-17 13:16:16 +08:00
parent 84759556ff
commit befd344ab0
28070 changed files with 4008428 additions and 1 deletions
+235
View File
@@ -0,0 +1,235 @@
# 8.0.0 - 2018-08-09
Changed: updated postcss 6.0.1 > 7.0.2, postcss-import 10.0.0 > 12.0.0, and required nodejs version ([PR#126](https://github.com/postcss/postcss-url/pull/126))
Changed: updated mime package
# 7.3.2 - 2018-04-03
Fixed: ignore urls which starts with `~` ([PR#119](https://github.com/postcss/postcss-url/pull/119))
# 7.3.1 - 2018-02-25
Fixed: ignore urls which starts with `//` ([PR#117](https://github.com/postcss/postcss-url/pull/117))
# 7.3.0 - 2017-10-26
Added: hash option - `append` ([PR#114](https://github.com/postcss/postcss-url/pull/114))
# 7.2.1 - 2017-10-19
Fixed: dependency security ([#108](https://github.com/postcss/postcss-url/issues/108)) ([#109](https://github.com/postcss/postcss-url/issues/109))
# 7.2.0 - 2017-10-17
Added: `assetsPath` option for `rebase`
# 7.1.2 - 2017-08-11
Fixed: wrap url by quotes for inlined svg ([#78](https://github.com/postcss/postcss-url/issues/78))
# 7.1.1 - 2017-07-24
Fixed: force wrap url by quotes for `optimizeSvgEncode` ([#105](https://github.com/postcss/postcss-url/issues/105))
# 7.1.0 - 2017-07-19
Added: `optimizeSvgEncode` option for inlined svg ([#103](https://github.com/postcss/postcss-url/issues/103))
Added: `rebase` as fallback in copy ([#104](https://github.com/postcss/postcss-url/issues/104))
# 7.0.0 - 2017-06-05
Added: PostCss 6 support
# 6.3.0 - 2017-06-04
Added: `multi` property for `custom`
Added: option to include fragment identifiers on inline data URIs
Added: support ignoring SVG fragment inlining warning
# 6.1.0 - 2017-05-13
Changed: filter functions access to asset object
Added: support crypto hash function methods
Added: support for postcss's dependency messaging
# 6.0.4 - 2017-04-06
Fixed: prepare asset without file path in decl
([#94](https://github.com/postcss/postcss-url/issues/94))
# 6.0.3 - 2017-04-04
Fixed: hash url error
([#89](https://github.com/postcss/postcss-url/issues/89))
# 6.0.2 - 2017-04-04
Fixed: match options before analyzing
([pull-88](https://github.com/postcss/postcss-url/pull/88))
# 6.0.1 - 2017-04-03
- Fixed: bug with empty options
([#87](https://github.com/postcss/postcss-url/issues/87))
# 6.0.0 - 2017-04-02
- Changed: es5 -> es6
- Added: multiple options for postcss-url as array
- Added: multiple `basePath` as array
- Added: _copy_ accept `basePath` param
- Changed: hash function to xxhash
- Changed: arguments in custom url callback
- Changed: no processing callback in _inline_ without `maxSize`
- Changed: `filter` matches by asset path, relative to project (process.cwd)
- Changed: _copy_ can work without postcss `to` option, but required `assetPath`
# 5.1.2 - 2016-05-01
- Fixed: node 6 compatibility
([#68](https://github.com/postcss/postcss-url/issues/68))
# 5.1.1 - 2016-02-03
- Fixed: typo in an error message
([#62](https://github.com/postcss/postcss-url/pull/62))
# 5.1.0 - 2016-01-19
- Added: `filter` option
([#61](https://github.com/postcss/postcss-url/pull/61))
# 5.0.2 - 2015-10-12
- Fixed: now rebase url in old Internet Explorer filter
`progid:DXImageTransform.Microsoft.AlphaImageLoader()`
([#55](https://github.com/postcss/postcss-url/pull/55))
# 5.0.1 - 2015-10-04
- Fixed: windows compatibility
([#52](https://github.com/postcss/postcss-url/pull/52))
# 5.0.0 - 2015-09-07
- Removed: compatibility with postcss v4.x
([#45](https://github.com/postcss/postcss-url/pull/45))
- Added: compatibility with postcss v5.x
([#76](https://github.com/postcss/postcss-url/pull/45))
# 4.0.1 - 2015-08-06
- Fixed: copy/rename of hash and query string for filenames
([#40](https://github.com/postcss/postcss-url/pull/40))
# 4.0.0 - 2015-06-18
- Fixed: fallback callback is working again
([#33](https://github.com/postcss/postcss-url/pull/33))
- Changed: Messages are now passed via postcss messages api
(no more console.warn)
- Added: callbacks might have now postcss result object as last param.
Handy to send some messages.
# 3.3.0 - 2015-06-16
- Added: postcss ^4.1.x dependency
([#31](https://github.com/postcss/postcss-url/pull/31))
- Added: new options to url callback
([#32](https://github.com/postcss/postcss-url/pull/32))
# 3.2.0 - 2015-05-01
- Added: New `fallback` option to use if max size is exceeded or url contains a hash
([#30](https://github.com/postcss/postcss-url/pull/30))
# 3.1.0 - 2015-05-01
- Added: New copy value for `url` option
([#29](https://github.com/postcss/postcss-url/pull/29))
# 3.0.0 - 2015-03-02
- Changed: upgraded to postcss v4.1.x
# 2.1.1 - 2015-03-31
- Fixed: whitespace before and after url() value are now supported and preserved
([#27](https://github.com/postcss/postcss-url/pull/27))
# 2.1.0 - 2015-03-12
- Added: related postcss declaration object has been added as a 2nd parameter to the url callback for custom processing
# 2.0.2 - 2015-01-31
- Fixed: url that are just hashes are ignored completely
([#25](https://github.com/postcss/postcss-url/issues/25))
# 2.0.1 - 2015-01-31
- Fixed: url with hashes are ignored for inline mode only
([#23](https://github.com/postcss/postcss-url/pull/23))
# 2.0.0 - 2015-01-26
- Added: compatibility with postcss v4.x
- Removed: compatibility with postcss v3.x
# 1.3.1 - 2015-01-26
- Fixed: dependency issue related to "directory-encoder"
([#22](https://github.com/postcss/postcss-url/pull/22))
# 1.3.0 - 2015-01-26
- Changed: SVGs are now in plain text (not base64 encoded) ([3c04f7a](https://github.com/postcss/postcss-url/commit/3c04f7abb1c017dfef34d3ddb00a5b44d8af951f), [#18](https://github.com/postcss/postcss-url/issues/18))
- Fixed: URLs with hashes (e.g. SVG fragments) are now ignored ([c3a9abc](https://github.com/postcss/postcss-url/commit/c3a9abcbed33ede323e7dcd6708b8fdb6168462f), [#20](https://github.com/postcss/postcss-url/pull/20))
# 1.2.3 - 2015-01-10
- Use Node's native buffer.toString("base64"). The js-base64 library was producing incorrect base64 for certain files
([#17](https://github.com/postcss/postcss-url/pull/17))
# 1.2.2 - unpublished
# 1.2.1 - 2014-12-09
- Data URIs are ignored correctly
([#15](https://github.com/postcss/postcss-url/pull/15))
# 1.2.0 - 2014-12-04
- `url` now accept a function to allow custom transformation of the url string
- All absolute url protocols are now ignored (not just /https?/).
# 1.1.3 - 2014-12-04
- Fix absolute urls being mangled
([#13](https://github.com/postcss/postcss-url/issues/13))
# 1.1.2 - 2014-11-08
- Fix MaxSize issue
([#9](https://github.com/postcss/postcss-url/issues/9))
# 1.1.1 - 2014-10-30
- Fix bug which leads to not correct base64 code
# 1.1.0 - 2014-10-29
- Add `maxSize` (size in kbytes) and `basePath` (base path for images to inline) options for _inline_ mode.
# 1.0.2 - 2014-10-10
- Fix non-working base64 encoding
# 1.0.1 - 2014-10-09
- Fix paths for Windows
([#3](https://github.com/postcss/postcss-url/issue/3) via [#4](https://github.com/postcss/postcss-url/pull/4))
# 1.0.0 - 2014-08-24
First release
+20
View File
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Maxime Thirouin
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.
+264
View File
@@ -0,0 +1,264 @@
# postcss-url
[![Travis Build Status](https://img.shields.io/travis/postcss/postcss-url/master.svg?label=unix%20build)](https://travis-ci.org/postcss/postcss-url)
[![AppVeyor Build Status](https://img.shields.io/appveyor/ci/MoOx/postcss-url/master.svg?label=windows%20build)](https://ci.appveyor.com/project/MoOx/postcss-url)
[![dependencies Status](https://david-dm.org/postcss/postcss-url/status.svg)](https://david-dm.org/postcss/postcss-url)
[![devDependencies Status](https://david-dm.org/postcss/postcss-url/dev-status.svg)](https://david-dm.org/postcss/postcss-url?type=dev)
> [PostCSS](https://github.com/postcss/postcss) plugin to rebase, inline or copy on url().
## Installation
```console
$ npm install postcss-url
```
## Basic example - rebase
```js
// dependencies
const fs = require("fs")
const postcss = require("postcss")
const url = require("postcss-url")
// css to be processed
const css = fs.readFileSync("input.css", "utf8")
// process css
const output = postcss()
.use(url({
url: "rebase"
}))
.process(css, {
from: "src/stylesheet/index.css",
to: "dist/index.css"
})
```
before:
```css
.element {
background: url('images/sprite.png');
}
```
after:
```css
.element {
/* rebasing path by new destination */
background: url('../src/stylesheet/images/sprite.png');
}
```
## Inline
```js
// postcss-url options
const options = {
url: 'inline'
};
postcss()
.use(url(options))
.process(css, {
from: "src/stylesheet/index.css",
to: "dist/index.css"
})
```
before:
```css
.element {
background: url('/images/sprite.png');
filter: url('/images/circle.svg');
}
```
after:
```css
.element {
/* inlined png as base64 */
background: url('data:image/png;base64,R0lGODlhAQABAJH/AP///wAAAP///wAAACH/C0FET0JFOklSMS4');
/* inlined svg as encodeURIComponent */
filter: url('data:image/svg+xml,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%2F%3E');
}
```
## Copy
```js
// postcss-url options
const options = {
url: 'copy',
// base path to search assets from
basePath: path.resolve('node_modules/bootstrap'),
// dir to copy assets
assetsPath: 'img',
// using hash names for assets (generates from asset content)
useHash: true
};
postcss()
.use(url(options))
.process(css, {
from: "src/stylesheet/index.css",
to: "dist/index.css"
})
```
before:
```css
.element {
background: url('/images/sprite.png');
}
```
after:
```css
.element {
/* copy 'sprite.png' from 'node_modules/bootstrap/images/' to 'dist/img/' */
/* and rename it by hash function */
background: url('img/a2ds3kfu.png');
}
```
### Muiltiple options
process first matched option by default.
```multi: true``` in `custom` will processing with other options
```js
const options = [
{ filter: '**/assets/copy/*.png', url: 'copy', assetsPath: 'img', useHash: true },
{ filter: '**/assets/inline/*.svg', url: 'inline' },
{ filter: '**/assets/**/*.gif', url: 'rebase' },
// using custom function to build url
{ filter: 'cdn/**/*', url: (asset) => `https://cdn.url/${asset.url}` }
];
postcss().use(url(options))
```
Checkout [tests](test) for examples.
### Options combinations
* `rebase` - _default_
* `assetsPath` - directory to copy assets (relative to `to` or absolute)
* `inline`
* `basePath` - path or array of paths to search assets (relative to `from`, or absolute)
* `encodeType` - `base64`, `encodeURI`, `encodeURIComponent`
* `includeUriFragment` - include the fragment identifer at the end of the URI
* `maxSize` - file size in kbytes
* `fallback` - `copy`, `rebase` or custom function for files > `maxSize`
* `ignoreFragmentWarning` - do not warn when an SVG URL with a fragment is inlined
* `optimizeSvgEncode` - reduce size of inlined svg (IE9+, Android 3+)
* `copy`
* `basePath` - path or array of paths to search assets (relative to `from`, or absolute)
* `assetsPath` - directory to copy assets (relative to `to` or absolute)
* `useHash` - use filehash(xxhash) for naming
* `hashOptions` - options for hash function
* `custom {Function}`
* `multi` - processing with other options
### Options list
#### `url`
##### `rebase` - _(default)_
Allow you to fix `url()` according to postcss `to` and/or `from` options (rebase to `to` first if available, otherwise `from` or `process.cwd()`).
##### `inline`
Allow you to inline assets using base64 encoding. Can use postcss `from` option to find ressources.
##### `copy`
Allow you to copy and rebase assets according to postcss `to`, `assetsPath` and `from` options (`assetsPath` is relative to the option `to`).
##### `url: {Function}`
Custom transform function. Takes following arguments:
* `asset`
* `url` - original url
* `pathname` - url pathname (url without search or hash)
* `absolutePath` - absolute path to asset
* `relativePath` - current relative path to asset
* `search` - _search_ from `url`, ex. `?query=1` from `./image.png?query=1`
* `hash` - _hash_ from `url`, ex. `#spriteLink` from `../asset.svg#spriteLink`
* `dir`
* `from` - postcss option from
* `to` - postcss option to
* `file` - decl file path
* `options` - postcss-url matched options
* `decl` - related postcss declaration object
* `warn` - wrapped function `result.warn` for current `decl`
* `result` postcss result object
And should return the transformed url.
You can use this option to adjust urls for CDN.
#### `maxSize`
Specify the maximum file size to inline (in kbytes)
#### `ignoreFragmentWarning`
_(default: `false`)_
Do not warn when an SVG URL with a fragment is inlined.
PostCSS-URL does not support partial inlining. The entire SVG file will be inlined. By default a warning will be issued when this occurs.
**NOTE:** Only files less than the maximum size will be inlined.
#### `filter`
A regular expression e.g. `/\.svg$/`, a [minimatch string](https://github.com/isaacs/minimatch) e.g. `'**/*.svg'` or a custom filter function to determine wether a file should be inlined.
#### `fallback`
The url fallback method to use if max size is exceeded or url contains a hash.
Custom transform functions are supported.
#### `includeUriFragment`
_(default: `false`)_
Specifies whether the URL's fragment identifer value, if present, will be added
to the inlined data URI.
#### `basePath`
Specify the base path or list of base paths where to search images from
#### `assetsPath`
_(default: `false`)_
If you specify an `assetsPath`, the assets files will be copied in that
destination
#### `useHash`
_(default: `false`)_
If set to `true` the copy method is going to rename the path of the files by a hash name
#### `hashOptions`
##### `method`
_(default: `xxhash32`)_
Hash method `xxhash32|xxhash64` or custom function (accept file buffer)
##### `shrink`
_(default: `8`)_
Result hash shrink count
##### `append`
_(default: `false`)_
Prepend the original filename in resulting filename
---
## Contributing
Work on a branch, install dev-dependencies, respect coding style & run tests before submitting a bug fix or a feature.
```console
$ git clone https://github.com/postcss/postcss-url.git
$ git checkout -b patch-1
$ npm install
$ npm test
```
## [Changelog](CHANGELOG.md)
## [License](LICENSE)
+82
View File
@@ -0,0 +1,82 @@
{
"_args": [
[
"postcss-url@8.0.0",
"/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series"
]
],
"_from": "postcss-url@8.0.0",
"_id": "postcss-url@8.0.0",
"_inBundle": false,
"_integrity": "sha512-E2cbOQ5aii2zNHh8F6fk1cxls7QVFZjLPSrqvmiza8OuXLzIpErij8BDS5Y3STPfJgpIMNCPEr8JlKQWEoozUw==",
"_location": "/postcss-url",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "postcss-url@8.0.0",
"name": "postcss-url",
"escapedName": "postcss-url",
"rawSpec": "8.0.0",
"saveSpec": null,
"fetchSpec": "8.0.0"
},
"_requiredBy": [
"/@nuxt/webpack"
],
"_resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-8.0.0.tgz",
"_spec": "8.0.0",
"_where": "/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series",
"author": {
"name": "Maxime Thirouin"
},
"bugs": {
"url": "https://github.com/postcss/postcss-url/issues"
},
"dependencies": {
"mime": "^2.3.1",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.0",
"postcss": "^7.0.2",
"xxhashjs": "^0.2.1"
},
"description": "PostCSS plugin to rebase or inline on url().",
"devDependencies": {
"chai": "^4.1.2",
"eslint": "^5.3.0",
"mocha": "^5.2.0",
"npmpub": "^4.1.0",
"postcss-import": "^12.0.0"
},
"engines": {
"node": ">=6.0.0"
},
"files": [
"src"
],
"homepage": "https://github.com/postcss/postcss-url#readme",
"keywords": [
"css",
"postcss",
"postcss-plugin",
"url",
"rebase",
"inline",
"base64",
"assets"
],
"license": "MIT",
"main": "src/index.js",
"name": "postcss-url",
"repository": {
"type": "git",
"url": "git+https://github.com/postcss/postcss-url.git"
},
"scripts": {
"lint": "eslint --fix .",
"release": "npmpub",
"test": "npm run lint && npm run tests",
"tests": "mocha"
},
"version": "8.0.0"
}
+52
View File
@@ -0,0 +1,52 @@
'use strict';
const path = require('path');
const postcss = require('postcss');
const declProcessor = require('./lib/decl-processor').declProcessor;
/**
*
* @type {Plugin}
*/
module.exports = postcss.plugin('postcss-url', (options) => {
options = options || {};
return function(styles, result) {
const opts = result.opts;
const from = opts.from ? path.dirname(opts.from) : '.';
const to = opts.to ? path.dirname(opts.to) : from;
styles.walkDecls((decl) =>
declProcessor(from, to, options, result, decl)
);
};
});
/**
* @callback PostcssUrl~UrlProcessor
* @param {String} from from
* @param {String} dirname to dirname
* @param {String} oldUrl url
* @param {String} to destination
* @param {Object} options plugin options
* @param {Object} decl postcss declaration
* @return {String|undefined} new url or undefined if url is old
*/
/**
* @typedef {Object} PostcssUrl~HashOptions
* @property {Function|String} [method=^xxhash32|xxhash64] - hash name or custom function, accepting file content
* @see https://github.com/pierrec/js-xxhash
* @property {Number} [shrink=8] - shrink hash string
*/
/**
* @typedef {Object} Decl - postcss decl
* @see http://api.postcss.org/Declaration.html
*/
/**
* @typedef {Object} Result - postcss result
* @see http://api.postcss.org/Result.html
*/
+151
View File
@@ -0,0 +1,151 @@
'use strict';
const matchOptions = require('./match-options');
const paths = require('./paths');
const getPathDeclFile = paths.getPathDeclFile;
const getDirDeclFile = paths.getDirDeclFile;
const prepareAsset = paths.prepareAsset;
/**
* @typedef UrlRegExp
* @name UrlRegExp
* @desc A regex for match url with parentheses:
* (before url)(the url)(after url).
* (the url) will be replace with new url, and before and after will remain
* @type RegExp
*/
/**
* @type {UrlRegExp[]}
*/
const URL_PATTERNS = [
/(url\(\s*['"]?)([^"')]+)(["']?\s*\))/g,
/(AlphaImageLoader\(\s*src=['"]?)([^"')]+)(["'])/g
];
const WITH_QUOTES = /^['"]/;
/**
* Restricted modes
*
* @type {String[]}
*/
const PROCESS_TYPES = ['rebase', 'inline', 'copy', 'custom'];
const getUrlProcessorType = (optionUrl) =>
typeof optionUrl === 'function' ? 'custom' : (optionUrl || 'rebase');
/**
* @param {String} optionUrl
* @returns {PostcssUrl~UrlProcessor}
*/
function getUrlProcessor(optionUrl) {
const mode = getUrlProcessorType(optionUrl);
if (PROCESS_TYPES.indexOf(mode) === -1) {
throw new Error(`Unknown mode for postcss-url: ${mode}`);
}
return require(`../type/${mode}`);
}
/**
* @param {PostcssUrl~UrlProcessor} urlProcessor
* @param {Result} result
* @param {Decl} decl
* @returns {Function}
*/
const wrapUrlProcessor = (urlProcessor, result, decl) => {
const warn = (message) => decl.warn(result, message);
const addDependency = (file) => result.messages.push({
type: 'dependency',
file,
parent: getPathDeclFile(decl)
});
return (asset, dir, option) =>
urlProcessor(asset, dir, option, decl, warn, result, addDependency);
};
/**
* @param {Decl} decl
* @returns {RegExp}
*/
const getPattern = (decl) =>
URL_PATTERNS.find((pattern) => pattern.test(decl.value));
/**
* @param {String} url
* @param {Dir} dir
* @param {Options} options
* @param {Result} result
* @param {Decl} decl
* @returns {String|undefined}
*/
const replaceUrl = (url, dir, options, result, decl) => {
const asset = prepareAsset(url, dir, decl);
const matchedOptions = matchOptions(asset, options);
if (!matchedOptions) return;
const process = (option) => {
const wrappedUrlProcessor = wrapUrlProcessor(getUrlProcessor(option.url), result, decl);
return wrappedUrlProcessor(asset, dir, option);
};
if (Array.isArray(matchedOptions)) {
matchedOptions.forEach((option) => asset.url = process(option));
} else {
asset.url = process(matchedOptions);
}
return asset.url;
};
/**
* @param {String} from
* @param {String} to
* @param {PostcssUrl~Options} options
* @param {Result} result
* @param {Decl} decl
* @returns {PostcssUrl~DeclProcessor}
*/
const declProcessor = (from, to, options, result, decl) => {
const dir = { from, to, file: getDirDeclFile(decl) };
const pattern = getPattern(decl);
if (!pattern) return;
decl.value = decl.value
.replace(pattern, (matched, before, url, after) => {
const newUrl = replaceUrl(url, dir, options, result, decl);
if (!newUrl) return matched;
if (WITH_QUOTES.test(newUrl) && WITH_QUOTES.test(after)) {
before = before.slice(0, -1);
after = after.slice(1);
}
return `${before}${newUrl}${after}`;
});
};
module.exports = {
replaceUrl,
declProcessor
};
/**
* @typedef {Object} PostcssUrl~Options - postcss-url Options
* @property {String} [url=^rebase|inline|copy|custom] - processing mode
* @property {Minimatch|RegExp|Function} [filter] - filter assets by relative pathname
* @property {String} [assetsPath] - absolute or relative path to copy assets
* @property {String|String[]} [basePath] - absolute or relative paths to search, when copy or inline
* @property {Number} [maxSize] - max file size in kbytes for inline mode
* @property {String} [fallback] - fallback mode if file exceeds maxSize
* @property {Boolean} [useHash] - use file hash instead filename
* @property {HashOptions} [hashOptions] - params for generating hash name
*/
+55
View File
@@ -0,0 +1,55 @@
'use strict';
/**
* Optimize encoding SVG files (IE9+, Android 3+)
* @see https://codepen.io/tigt/post/optimizing-svgs-in-data-uris
*
* @param {String} svgContent
* @returns {String}
*/
const optimizedSvgEncode = (svgContent) => {
const result = encodeURIComponent(svgContent)
.replace(/%3D/g, '=')
.replace(/%3A/g, ':')
.replace(/%2F/g, '/')
.replace(/%22/g, "'")
.replace(/%2C/g, ',')
.replace(/%3B/g, ';');
// Lowercase the hex-escapes for better gzipping
return result.replace(/(%[0-9A-Z]{2})/g, (matched, AZ) => {
return AZ.toLowerCase();
});
};
/**
* Encoding file contents to string
*
* @param {PostcssUrl~File} file
* @param {String} [encodeType=base64|encodeURI|encodeURIComponent]
* @param {Boolean} [shouldOptimizeURIEncode]
* @returns {string}
*/
module.exports = (file, encodeType, shouldOptimizeSvgEncode) => {
const dataMime = `data:${file.mimeType}`;
if (encodeType === 'base64') {
return `${dataMime};base64,${file.contents.toString('base64')}`;
}
const encodeFunc = encodeType === 'encodeURI' ? encodeURI : encodeURIComponent;
const content = file.contents.toString('utf8')
// removing new lines
.replace(/\n+/g, '');
let encodedStr = (shouldOptimizeSvgEncode && encodeType === 'encodeURIComponent')
? optimizedSvgEncode(content)
: encodeFunc(content);
encodedStr = encodedStr
.replace(/%20/g, ' ')
.replace(/#/g, '%23');
return `${dataMime},${encodedStr}`;
};
+42
View File
@@ -0,0 +1,42 @@
'use strict';
const fs = require('fs');
const mime = require('mime');
const getPathByBasePath = require('./paths').getPathByBasePath;
/**
*
* @param {PostcssUrl~Asset} asset
* @param {PostcssUrl~Options} options
* @param {PostcssUrl~Dir} dir
* @param {Function} warn
* @returns {PostcssUrl~File}
*/
const getFile = (asset, options, dir, warn) => {
const paths = options.basePath
? getPathByBasePath(options.basePath, dir.from, asset.pathname)
: [asset.absolutePath];
const filePath = paths.find(fs.existsSync);
if (!filePath) {
warn(`Can't read file '${paths.join()}', ignoring`);
return;
}
return {
path: filePath,
contents: fs.readFileSync(filePath),
mimeType: mime.getType(filePath)
};
};
module.exports = getFile;
/**
* @typedef {Object} PostcssUrl~File
* @property {String} path
* @property {Buffer} contents
* @property {String} mimeType
*/
+54
View File
@@ -0,0 +1,54 @@
'use strict';
const crypto = require('crypto');
const xxh = require('xxhashjs');
const HEXBASE = 16;
const defaultHashOptions = {
method: 'xxhash32',
shrink: 8,
append: false
};
const getxxhash = (content, options) => {
const hashFunc = options.method === 'xxhash32' ? xxh.h32 : xxh.h64;
const seed = 0;
return hashFunc(seed)
.update(content)
.digest()
.toString(HEXBASE);
};
const getHash = (content, options) => {
if (options.method && typeof options.method === 'function') {
return options.method(content);
}
if (options.method && options.method.indexOf('xxhash') === 0) {
return getxxhash(content, options);
}
try {
const hashFunc = crypto.createHash(options.method);
return hashFunc.update(content)
.digest('hex');
} catch (e) {
return null;
}
};
module.exports = function(content, options) {
options = options || defaultHashOptions;
let hash = getHash(content, options);
if (hash == null) {
// bad hash method; fallback to defaults
// TODO: warning/error reporting?
hash = getHash(content, defaultHashOptions);
}
return options.shrink ? hash.substr(0, options.shrink) : hash;
};
+92
View File
@@ -0,0 +1,92 @@
'use strict';
const minimatch = require('minimatch');
const path = require('path');
const paths = require('./paths');
const isUrlShouldBeIgnored = paths.isUrlShouldBeIgnored;
/**
* Returns whether the given asset matches the given pattern
* Allways returns true if the given pattern is empty
*
* @param {PostcssUrl~Asset} asset the processed asset
* @param {String|RegExp|Function} pattern A minimatch string,
* regular expression or function to test the asset
*
* @returns {Boolean}
*/
const matchesFilter = (asset, pattern) => {
const relativeToRoot = path.relative(process.cwd(), asset.absolutePath);
if (typeof pattern === 'string') {
pattern = minimatch.filter(pattern);
return pattern(relativeToRoot);
}
if (pattern instanceof RegExp) {
return pattern.test(relativeToRoot);
}
if (pattern instanceof Function) {
return pattern(asset);
}
return true;
};
/**
* Matching single option
*
* @param {PostcssUrl~Asset} asset
* @param {PostcssUrl~Options} option
* @returns {Boolean}
*/
const matchOption = (asset, option) => {
const matched = matchesFilter(asset, option.filter);
if (!matched) return false;
return typeof option.url === 'function' || !isUrlShouldBeIgnored(asset.url, option);
};
const isMultiOption = (option) =>
option.multi && typeof option.url === 'function';
/**
* Matching options by asset
*
* @param {PostcssUrl~Asset} asset
* @param {PostcssUrl~Options|PostcssUrl~Options[]} options
* @returns {PostcssUrl~Options|undefined}
*/
const matchOptions = (asset, options) => {
if (!options) return;
if (Array.isArray(options)) {
const optionIndex = options.findIndex((option) => matchOption(asset, option));
if (optionIndex < 0) return;
const matchedOption = options[optionIndex];
// if founded option is last
if (optionIndex === options.length - 1) return matchedOption;
const extendOptions = options
.slice(optionIndex + 1)
.filter((option) =>
(isMultiOption(matchedOption) || isMultiOption(option)) && matchOption(asset, option)
);
return extendOptions.length
? [matchedOption].concat(extendOptions)
: matchedOption;
}
if (matchOption(asset, options)) return options;
};
module.exports = matchOptions;
+155
View File
@@ -0,0 +1,155 @@
'use strict';
const path = require('path');
const url = require('url');
/**
* Normalazing result url, before replace decl value
*
* @param {String} assetUrl
* @returns {String}
*/
const normalize = (assetUrl) => {
assetUrl = path.normalize(assetUrl);
return path.sep === '\\' ? assetUrl.replace(/\\/g, '\/') : assetUrl;
};
/**
* @param {String} assetUrl
* @returns {Boolean}
*/
const isUrlWithoutPathname = (assetUrl) => {
return assetUrl[0] === '#'
|| assetUrl.indexOf('%23') === 0
|| assetUrl.indexOf('data:') === 0
|| /^[a-z]+:\/\//.test(assetUrl)
|| /^\/\//.test(assetUrl);
};
/**
* Check if url is absolute, hash or data-uri
*
* @param {String} assetUrl
* @param {PostcssUrl~Options} options
* @returns {Boolean}
*/
const isUrlShouldBeIgnored = (assetUrl, options) => {
const isAbsolutePath = assetUrl[0] === '/';
const isStartsWithTilde = assetUrl[0] === '~';
return isUrlWithoutPathname(assetUrl) || ((isAbsolutePath || isStartsWithTilde) && !options.basePath);
};
/**
* @param {String} baseDir - absolute target path
* @param {String} assetsPath - extend asset path, can be absolute path
* @param {String} relative - current relative asset path
* @returns {String}
*/
const getAssetsPath = (baseDir, assetsPath, relative) =>
path.resolve(baseDir, assetsPath || '', relative || '');
/**
* Target path, output base dir
*
* @param {Dir} dir
* @returns {String}
*/
const getTargetDir = (dir) =>
dir.from !== dir.to ? dir.to : process.cwd();
/**
* Stylesheet file path from decl
*
* @param {Decl} decl
* @returns {String}
*/
const getPathDeclFile = (decl) =>
decl.source && decl.source.input && decl.source.input.file;
/**
* Stylesheet file dir from decl
*
* @param {Decl} decl
* @returns {String}
*/
const getDirDeclFile = (decl) => {
const filename = getPathDeclFile(decl);
return filename ? path.dirname(filename) : process.cwd();
};
/**
* Returns paths list, where we can find assets file
*
* @param {String[]|String} basePath - base paths where trying search to assets file
* @param {Dir} dirFrom
* @param {String} relPath - relative asset path
* @returns {String[]}
*/
const getPathByBasePath = (basePath, dirFrom, relPath) => {
if (relPath[0] === '/') {
relPath = `.${relPath}`;
}
basePath = !Array.isArray(basePath) ? [basePath] : basePath;
return basePath.map((pathItem) =>
getAssetsPath(dirFrom, pathItem, relPath)
);
};
/**
* Preparing asset paths and data
*
* @param {String} assetUrl
* @param {PostcssUrl~Dir} dir
* @param {Decl} decl
* @returns {PostcssUrl~Asset}
*/
const prepareAsset = (assetUrl, dir, decl) => {
const parsedUrl = url.parse(assetUrl);
const pathname = !isUrlWithoutPathname(assetUrl) ? parsedUrl.pathname : null;
const absolutePath = pathname
? path.resolve(path.join(dir.file, pathname))
: getPathDeclFile(decl);
return {
url: assetUrl,
originUrl: assetUrl,
pathname,
absolutePath: absolutePath || dir.from,
relativePath: absolutePath ? path.relative(dir.from, absolutePath) : '.',
search: (parsedUrl.search || ''),
hash: (parsedUrl.hash || '')
};
};
module.exports = {
normalize,
prepareAsset,
getAssetsPath,
getDirDeclFile,
getPathDeclFile,
getTargetDir,
getPathByBasePath,
isUrlShouldBeIgnored
};
/**
* @typedef {Object} PostcssUrl~Asset
* @property {String} url - origin asset url
* @property {String} name - parsed asset filename
* @property {String} absolutePath - absolute asset path
* @property {String} relativePath - relative asset path (relative to target dir)
* @property {String} search - search from url, ex. ?query=1
* @property {String} hash - hash from url
*/
/**
* @typedef {Object} PostcssUrl~Dir
* @property {String} from - dirname from postcss option 'from'
* @property {String} to - dirname from postcss option 'to'
* @property {String} file - decl file dirname (css file)
*/
+69
View File
@@ -0,0 +1,69 @@
'use strict';
const path = require('path');
const fs = require('fs');
const mkdirp = require('mkdirp');
const calcHash = require('../lib/hash');
const paths = require('../lib/paths');
const getFile = require('../lib/get-file');
const getTargetDir = paths.getTargetDir;
const getAssetsPath = paths.getAssetsPath;
const normalize = paths.normalize;
const getHashName = (file, options) =>
(options && options.append ? (`${path.basename(file.path, path.extname(file.path))}_`) : '')
+ calcHash(file.contents, options)
+ path.extname(file.path);
/**
* Copy images from readed from url() to an specific assets destination
* (`assetsPath`) and fix url() according to that path.
* You can rename the assets by a hash or keep the real filename.
*
* Option assetsPath is require and is relative to the css destination (`to`)
*
* @type {PostcssUrl~UrlProcessor}
* @param {PostcssUrl~Asset} asset
* @param {PostcssUrl~Dir} dir
* @param {PostcssUrl~Option} options
* @param {PostcssUrl~Decl} decl
* @param {Function} warn
* @param {Result} result
* @param {Function} addDependency
*
* @returns {String|Undefined}
*/
module.exports = function processCopy(asset, dir, options, decl, warn, result, addDependency) {
if (!options.assetsPath && dir.from === dir.to) {
warn('Option `to` of postcss is required, ignoring');
return;
}
const file = getFile(asset, options, dir, warn);
if (!file) return;
const assetRelativePath = options.useHash
? getHashName(file, options.hashOptions)
: asset.relativePath;
const targetDir = getTargetDir(dir);
const newAssetBaseDir = getAssetsPath(targetDir, options.assetsPath);
const newAssetPath = path.join(newAssetBaseDir, assetRelativePath);
const newRelativeAssetPath = normalize(
path.relative(targetDir, newAssetPath)
);
mkdirp.sync(path.dirname(newAssetPath));
if (!fs.existsSync(newAssetPath)) {
fs.writeFileSync(newAssetPath, file.contents);
}
addDependency(file.path);
return `${newRelativeAssetPath}${asset.search}${asset.hash}`;
};
+13
View File
@@ -0,0 +1,13 @@
/**
* Transform url() based on a custom callback
*
* @type {PostcssUrl~UrlProcessor}
* @param {PostcssUrl~Asset} asset
* @param {PostcssUrl~Dir} dir
* @param {PostcssUrl~Option} options
*
* @returns {String|Undefined}
*/
module.exports = function getCustomProcessor(asset, dir, options) {
return options.url.apply(null, arguments);
};
+88
View File
@@ -0,0 +1,88 @@
'use strict';
const fs = require('fs');
const processCopy = require('./copy');
const processRebase = require('./rebase');
const encodeFile = require('../lib/encode');
const getFile = require('../lib/get-file');
/**
* @param {String} originUrl
* @param {PostcssUrl~Dir} dir
* @param {PostcssUrl~Option} options
*
* @returns {String|Undefined}
*/
function processFallback(originUrl, dir, options) {
if (typeof options.fallback === 'function') {
return options.fallback.apply(null, arguments);
}
switch (options.fallback) {
case 'copy':
return processCopy.apply(null, arguments);
case 'rebase':
return processRebase.apply(null, arguments);
default:
return;
}
}
/**
* Inline image in url()
*
* @type {PostcssUrl~UrlProcessor}
* @param {PostcssUrl~Asset} asset
* @param {PostcssUrl~Dir} dir
* @param {PostcssUrl~Options} options
* @param {PostcssUrl~Decl} decl
* @param {Function} warn
* @param {Result} result
* @param {Function} addDependency
*
* @returns {String|Undefined}
*/
// eslint-disable-next-line complexity
module.exports = function(asset, dir, options, decl, warn, result, addDependency) {
const file = getFile(asset, options, dir, warn);
if (!file) return;
if (!file.mimeType) {
warn(`Unable to find asset mime-type for ${file.path}`);
return;
}
const maxSize = (options.maxSize || 0) * 1024;
if (maxSize) {
const stats = fs.statSync(file.path);
if (stats.size >= maxSize) {
return processFallback.apply(this, arguments);
}
}
const isSvg = file.mimeType === 'image/svg+xml';
const defaultEncodeType = isSvg ? 'encodeURIComponent' : 'base64';
const encodeType = options.encodeType || defaultEncodeType;
// Warn for svg with hashes/fragments
if (isSvg && asset.hash && !options.ignoreFragmentWarning) {
// eslint-disable-next-line max-len
warn(`Image type is svg and link contains #. Postcss-url cant handle svg fragments. SVG file fully inlined. ${file.path}`);
}
addDependency(file.path);
const optimizeSvgEncode = isSvg && options.optimizeSvgEncode;
const encodedStr = encodeFile(file, encodeType, optimizeSvgEncode);
const resultValue = options.includeUriFragment && asset.hash
? encodedStr + asset.hash
: encodedStr;
// wrap url by quotes if percent-encoded svg
return isSvg && encodeType !== 'base64' ? `"${resultValue}"` : resultValue;
};
+25
View File
@@ -0,0 +1,25 @@
'use strict';
const path = require('path');
const paths = require('../lib/paths');
const normalize = paths.normalize;
const getAssetsPath = paths.getAssetsPath;
/**
* Fix url() according to source (`from`) or destination (`to`)
*
* @type {PostcssUrl~UrlProcessor}
* @param {PostcssUrl~Asset} asset
* @param {PostcssUrl~Dir} dir
* @param {PostcssUrl~Option} options
*
* @returns {String|Undefined}
*/
module.exports = function(asset, dir, options) {
const dest = getAssetsPath(dir.to, options && options.assetsPath || '');
const rebasedUrl = normalize(
path.relative(dest, asset.absolutePath)
);
return `${rebasedUrl}${asset.search}${asset.hash}`;
};