Skip to content
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
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8.9
12
19 changes: 8 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,27 @@
"devDependencies": {
"blue-tape": "^1.0.0",
"compose-function": "^3.0.3",
"cross-env": "^6.0.3",
"cross-env": "^7.0.3",
"enforce-node-version": "^0.1.0",
"es6-promisify": "^6.0.2",
"escape-string-regexp": "^2.0.0",
"escape-string-regexp": "^4.0.0",
"get-value": "^3.0.1",
"has-prop": "^0.1.2",
"joi": "^14.3.1",
"jshint": "^2.10.3",
"micromatch": "^4.0.2",
"ms": "^2.1.2",
"outdent": "^0.7.0",
"micromatch": "^4.0.4",
"ms": "^2.1.3",
"outdent": "^0.8.0",
"promise-compose": "^1.1.2",
"recursive-readdir": "^2.2.2",
"rework": "1.0.1",
"rework-visit": "1.0.0",
"sinon": "^8.0.2",
"sinon": "^10.0.0",
"tap-diff": "^0.1.1",
"vlq": "^1.0.1"
},
"engines": {
"node": ">=8.9"
"node": ">=12"
},
"resolutions": {
"diff": "^3.5.0",
"lodash": "^4.17.21"
"diff": "^4.0.2"
}
}
2 changes: 1 addition & 1 deletion packages/resolve-url-loader-filesearch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
},
"homepage": "https://github.com/bholloway/resolve-url-loader",
"engines": {
"node": ">=8.9"
"node": ">=12"
},
"files": [
"index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/resolve-url-loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var valueProcessor = require('./lib/value-processor'),
const DEPRECATED_OPTIONS = {
engine: [
'DEP_RESOLVE_URL_LOADER_OPTION_ENGINE',
'the "engine" option is deprecated, "postcss" engine is the default, using "rework" engine is not advised'
'the "engine" option is deprecated, "postcss" engine is the default, there are no other available engines'
],
keepQuery: [
'DEP_RESOLVE_URL_LOADER_OPTION_KEEP_QUERY',
Expand Down
171 changes: 90 additions & 81 deletions packages/resolve-url-loader/lib/engine/postcss.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
*/
'use strict';

var os = require('os'),
path = require('path'),
postcss = require('postcss');
const os = require('os');
const path = require('path');
const postcss = require('postcss');

var fileProtocol = require('../file-protocol');
var algerbra = require('../position-algerbra');
const fileProtocol = require('../file-protocol');
const algerbra = require('../position-algerbra');

var ORPHAN_CR_REGEX = /\r(?!\n)(.|\n)?/g;
const ORPHAN_CR_REGEX = /\r(?!\n)(.|\n)?/g;

/**
* Process the given CSS content into reworked CSS content.
Expand All @@ -24,66 +24,96 @@ var ORPHAN_CR_REGEX = /\r(?!\n)(.|\n)?/g;
*/
function process(sourceFile, sourceContent, params) {
// #107 libsass emits orphan CR not considered newline, postcss does consider newline (content vs source-map mismatch)
var correctedContent = params.removeCR && (os.EOL !== '\r') ?
const correctedContent = params.removeCR && (os.EOL !== '\r') ?
sourceContent.replace(ORPHAN_CR_REGEX, ' $1') :
sourceContent;

/**
* Plugin for postcss that follows SASS transpilation.
*/
const postcssPlugin = {
postcssPlugin: 'postcss-resolve-url',
Declaration: (declaration) => {
var prefix,
isValid = declaration.value && (declaration.value.indexOf('url') >= 0);
if (isValid) {
prefix = declaration.prop + declaration.raws.between;
declaration.value = params.transformDeclaration(declaration.value, getPathsAtChar);
}
// IMPORTANT - prepend file protocol to all sources to avoid problems with source map
const plugin = Object.assign(
() => ({
postcssPlugin: 'postcss-resolve-url',
prepare: () => {
const visited = new Set();

/**
* Given an apparent position find the directory of the original file.
*
* @param startPosApparent {{line: number, column: number}}
* @returns {false|string} Directory of original file or false on invalid
*/
const positionToOriginalDirectory = (startPosApparent) => {
// reverse the original source-map to find the original source file before transpilation
const startPosOriginal =
!!params.sourceMapConsumer &&
params.sourceMapConsumer.originalPositionFor(startPosApparent);

/**
* Create a hash of base path strings.
*
* Position in the declaration is supported by postcss at the position of the url() statement.
*
* @param {number} index Index in the declaration value at which to evaluate
* @throws Error on invalid source map
* @returns {{subString:string, value:string, property:string, selector:string}} Hash of base path strings
*/
function getPathsAtChar(index) {
var subString = declaration.value.slice(0, index),
posSelector = algerbra.sanitise(declaration.parent.source.start),
posProperty = algerbra.sanitise(declaration.source.start),
posValue = algerbra.add([posProperty, algerbra.strToOffset(prefix)]),
posSubString = algerbra.add([posValue, algerbra.strToOffset(subString)]);
// we require a valid directory for the specified file
const directory =
!!startPosOriginal &&
!!startPosOriginal.source &&
fileProtocol.remove(path.dirname(startPosOriginal.source));

var result = {
subString: positionToOriginalDirectory(posSubString),
value : positionToOriginalDirectory(posValue),
property : positionToOriginalDirectory(posProperty),
selector : positionToOriginalDirectory(posSelector)
return directory;
};

var isValid = [result.subString, result.value, result.property, result.selector].every(Boolean);
if (isValid) {
return result;
}
else if (params.sourceMapConsumer) {
throw new Error(
'source-map information is not available at url() declaration ' +
(ORPHAN_CR_REGEX.test(sourceContent) ? '(found orphan CR, try removeCR option)' : '(no orphan CR found)')
);
} else {
throw new Error('a valid source-map is not present (ensure preceding loaders output a source-map)');
}
return {
Declaration: (declaration) => {
var prefix,
isValid = declaration.value && (declaration.value.indexOf('url') >= 0) && !visited.has(declaration);
if (isValid) {
prefix = declaration.prop + declaration.raws.between;
declaration.value = params.transformDeclaration(declaration.value, getPathsAtChar);
visited.add(declaration);
}

/**
* Create a hash of base path strings.
*
* Position in the declaration is supported by postcss at the position of the url() statement.
*
* @param {number} index Index in the declaration value at which to evaluate
* @throws Error on invalid source map
* @returns {{subString:string, value:string, property:string, selector:string}} Hash of base path strings
*/
function getPathsAtChar(index) {
var subString = declaration.value.slice(0, index),
posSelector = algerbra.sanitise(declaration.parent.source.start),
posProperty = algerbra.sanitise(declaration.source.start),
posValue = algerbra.add([posProperty, algerbra.strToOffset(prefix)]),
posSubString = algerbra.add([posValue, algerbra.strToOffset(subString)]);

var result = {
subString: positionToOriginalDirectory(posSubString),
value : positionToOriginalDirectory(posValue),
property : positionToOriginalDirectory(posProperty),
selector : positionToOriginalDirectory(posSelector)
};

var isValid = [result.subString, result.value, result.property, result.selector].every(Boolean);
if (isValid) {
return result;
}
else if (params.sourceMapConsumer) {
throw new Error(
'source-map information is not available at url() declaration ' + (
ORPHAN_CR_REGEX.test(sourceContent) ?
'(found orphan CR, try removeCR option)' :
'(no orphan CR found)'
)
);
} else {
throw new Error('a valid source-map is not present (ensure preceding loaders output a source-map)');
}
}
}
};
}
}
}
}),
{ postcss: true }
);

// prepend file protocol to all sources to avoid problems with source map
return postcss([
postcssPlugin
])
// IMPORTANT - prepend file protocol to all sources to avoid problems with source map
return postcss([plugin])
.process(correctedContent, {
from: fileProtocol.prepend(sourceFile),
map : params.outputSourceMap && {
Expand All @@ -93,31 +123,10 @@ function process(sourceFile, sourceContent, params) {
sourcesContent: true // #98 sourcesContent missing from output map
}
})
.then(result => ({
content: result.css,
map : params.outputSourceMap ? fileProtocol.remove(result.map.toJSON()) : null
.then(({css, map}) => ({
content: css,
map : params.outputSourceMap ? fileProtocol.remove(map.toJSON()) : null
}));

/**
* Given an apparent position find the directory of the original file.
*
* @param startPosApparent {{line: number, column: number}}
* @returns {false|string} Directory of original file or false on invalid
*/
function positionToOriginalDirectory(startPosApparent) {
// reverse the original source-map to find the original source file before transpilation
var startPosOriginal =
!!params.sourceMapConsumer &&
params.sourceMapConsumer.originalPositionFor(startPosApparent);

// we require a valid directory for the specified file
var directory =
!!startPosOriginal &&
!!startPosOriginal.source &&
fileProtocol.remove(path.dirname(startPosOriginal.source));

return directory;
}
}

module.exports = process;
Loading