express-compiless
Advanced tools
Comparing version 4.0.0 to 4.1.0
@@ -1,153 +0,218 @@ | ||
var Path = require('path'), | ||
crypto = require('crypto'), | ||
fs = require('fs'), | ||
async = require('async'), | ||
csserror = require('csserror'), | ||
passError = require('passerror'), | ||
hijackResponse = require('hijackresponse'); | ||
const Path = require('path'); | ||
const crypto = require('crypto'); | ||
const fs = require('fs'); | ||
const async = require('async'); | ||
const csserror = require('csserror'); | ||
const passError = require('passerror'); | ||
const hijackResponse = require('hijackresponse'); | ||
module.exports = function compiless(options) { | ||
if (!options || !options.root) { | ||
throw new Error('options.root is mandatory'); | ||
} | ||
if (!options || !options.root) { | ||
throw new Error('options.root is mandatory'); | ||
} | ||
var less = options.less || require('less'); | ||
var isOldLessApi = !less.version || less.version[0] < 2; | ||
var plugins = options.plugins || []; | ||
if (plugins.length > 0 && isOldLessApi) { | ||
throw new Error('Less version < 2.0.0 does not support plugins'); | ||
} | ||
var toCSSOptions = options.toCSSOptions || {}; | ||
const less = options.less || require('less'); | ||
const isOldLessApi = !less.version || less.version[0] < 2; | ||
const plugins = options.plugins || []; | ||
if (plugins.length > 0 && isOldLessApi) { | ||
throw new Error('Less version < 2.0.0 does not support plugins'); | ||
} | ||
const toCSSOptions = options.toCSSOptions || {}; | ||
function formatError(err) { // err can either be a string or an error object | ||
if (typeof err === 'string') { | ||
return err; | ||
} else { | ||
// Assume error object | ||
return err.message + ('line' in err ? ' at line ' + err.line + ('column' in err ? ', column ' + err.column : '') : '') + | ||
('extract' in err ? ':\n' + err.extract.join('\n') : ''); | ||
} | ||
function formatError(err) { | ||
// err can either be a string or an error object | ||
if (typeof err === 'string') { | ||
return err; | ||
} else { | ||
// Assume error object | ||
return ( | ||
err.message + | ||
('line' in err | ||
? ' at line ' + | ||
err.line + | ||
('column' in err ? ', column ' + err.column : '') | ||
: '') + | ||
('extract' in err ? ':\n' + err.extract.join('\n') : '') | ||
); | ||
} | ||
} | ||
return function (req, res, next) { | ||
// Prevent If-None-Match revalidation with the downstream middleware with ETags that aren't suffixed with "-compiless": | ||
var ifNoneMatch = req.headers['if-none-match']; | ||
return (req, res, next) => { | ||
// Prevent If-None-Match revalidation with the downstream middleware with ETags that aren't suffixed with "-compiless": | ||
const ifNoneMatch = req.headers['if-none-match']; | ||
// only hijack requests for .less files | ||
if (/\.less$/.test(req.originalUrl)) { | ||
if (ifNoneMatch) { | ||
var validIfNoneMatchTokens = ifNoneMatch.split(" ").filter(function (etag) { | ||
return /-compiless\"$/.test(etag); | ||
}); | ||
// only hijack requests for .less files | ||
if (/\.less$/.test(req.originalUrl)) { | ||
if (ifNoneMatch) { | ||
const validIfNoneMatchTokens = ifNoneMatch | ||
.split(' ') | ||
.filter(etag => /-compiless"$/.test(etag)); | ||
if (validIfNoneMatchTokens.length > 0) { | ||
req.headers['if-none-match'] = validIfNoneMatchTokens.join(" "); | ||
} else { | ||
delete req.headers['if-none-match']; | ||
} | ||
} | ||
delete req.headers['if-modified-since']; // Prevent false positive conditional GETs after enabling compiless | ||
if (validIfNoneMatchTokens.length > 0) { | ||
req.headers['if-none-match'] = validIfNoneMatchTokens.join(' '); | ||
} else { | ||
delete req.headers['if-none-match']; | ||
} | ||
} | ||
delete req.headers['if-modified-since']; // Prevent false positive conditional GETs after enabling compiless | ||
hijackResponse(res, function (err, res) { | ||
if (err) { | ||
res.unhijack(); | ||
return next(err); | ||
} | ||
hijackResponse( | ||
res, | ||
(err, res) => { | ||
if (err) { | ||
res.unhijack(); | ||
return next(err); | ||
} | ||
var contentType = res.getHeader('Content-Type'), | ||
matchContentType = contentType && contentType.match(/^text\/less(?:;\s*charset=([a-z0-9\-]+))?$/i); | ||
const contentType = res.getHeader('Content-Type'); | ||
const matchContentType = | ||
contentType && | ||
contentType.match(/^text\/less(?:;\s*charset=([a-z0-9-]+))?$/i); | ||
// The mime module doesn't support less yet, so we fall back: | ||
if (!(matchContentType || contentType === 'application/octet-stream')) { | ||
return res.unhijack(); | ||
} | ||
// The mime module doesn't support less yet, so we fall back: | ||
if ( | ||
!(matchContentType || contentType === 'application/octet-stream') | ||
) { | ||
return res.unhijack(); | ||
} | ||
var baseDir = Path.resolve(options.root, req.url.replace(/\/[^\/]*(?:\?.*)?$/, '/').substr(1)), | ||
filename = Path.resolve(options.root, req.url.substr(1)), | ||
lessOptions = { | ||
paths: [baseDir], | ||
filename: filename, | ||
plugins: plugins, | ||
relativeUrls: true | ||
}; | ||
const baseDir = Path.resolve( | ||
options.root, | ||
req.url.replace(/\/[^/]*(?:\?.*)?$/, '/').substr(1) | ||
); | ||
const filename = Path.resolve(options.root, req.url.substr(1)); | ||
const lessOptions = { | ||
paths: [baseDir], | ||
filename, | ||
plugins, | ||
relativeUrls: true | ||
}; | ||
function sendErrorResponse(err, cssText) { | ||
var errorMessage = "express-compiless: Error compiling " + req.originalUrl + ":\n" + formatError(err); | ||
res.removeHeader('Content-Length'); | ||
res.removeHeader('ETag'); | ||
res.setHeader('Content-Type', 'text/css; charset=utf-8'); | ||
res.end(csserror(errorMessage) + '\n' + (cssText || '')); | ||
} | ||
function sendErrorResponse(err, cssText) { | ||
const errorMessage = | ||
'express-compiless: Error compiling ' + | ||
req.originalUrl + | ||
':\n' + | ||
formatError(err); | ||
res.removeHeader('Content-Length'); | ||
res.removeHeader('ETag'); | ||
res.setHeader('Content-Type', 'text/css; charset=utf-8'); | ||
res.end(csserror(errorMessage) + '\n' + (cssText || '')); | ||
} | ||
function respondWithCss(css, importedFileNames) { | ||
importedFileNames = importedFileNames || []; | ||
var statsByFileName = {}; | ||
async.eachLimit(importedFileNames, 10, function (importedFileName, cb) { | ||
fs.stat(importedFileName, passError(cb, function (stats) { | ||
statsByFileName[importedFileName] = stats; | ||
cb(); | ||
})); | ||
}, passError(sendErrorResponse, function () { | ||
var oldETag = res.getHeader('ETag'); | ||
function respondWithCss(css, importedFileNames) { | ||
importedFileNames = importedFileNames || []; | ||
const statsByFileName = {}; | ||
async.eachLimit( | ||
importedFileNames, | ||
10, | ||
(importedFileName, cb) => { | ||
fs.stat( | ||
importedFileName, | ||
passError(cb, stats => { | ||
statsByFileName[importedFileName] = stats; | ||
cb(); | ||
}) | ||
); | ||
}, | ||
passError(sendErrorResponse, function() { | ||
const oldETag = res.getHeader('ETag'); | ||
if (oldETag) { | ||
var oldETagIsWeak = oldETag && /^W\//.test(oldETag), | ||
etagFragments = [oldETag.replace(/^(?:W\/)?"|"$/g, '')]; | ||
if (oldETag) { | ||
const oldETagIsWeak = oldETag && /^W\//.test(oldETag); | ||
const etagFragments = [oldETag.replace(/^(?:W\/)?"|"$/g, '')]; | ||
if (importedFileNames.length) { | ||
var importedFileStats = []; | ||
if (importedFileNames.length) { | ||
const importedFileStats = []; | ||
importedFileNames.sort().forEach(function (importedFileName) { | ||
var stats = statsByFileName[importedFileName]; | ||
importedFileStats.push(importedFileName, String(stats.mtime.getTime()), String(stats.size)); | ||
}, this); | ||
importedFileNames.sort().forEach(importedFileName => { | ||
const stats = statsByFileName[importedFileName]; | ||
importedFileStats.push( | ||
importedFileName, | ||
String(stats.mtime.getTime()), | ||
String(stats.size) | ||
); | ||
}, this); | ||
etagFragments.push(crypto.createHash('md5').update(importedFileStats.join('-')).digest('hex').substr(0, 16)); | ||
} | ||
etagFragments.push( | ||
crypto | ||
.createHash('md5') | ||
.update(importedFileStats.join('-')) | ||
.digest('hex') | ||
.substr(0, 16) | ||
); | ||
} | ||
var newETag = (oldETagIsWeak ? 'W/' : '') + '"' + etagFragments.join('-') + '-compiless"'; | ||
res.setHeader('ETag', newETag); | ||
const newETag = | ||
(oldETagIsWeak ? 'W/' : '') + | ||
'"' + | ||
etagFragments.join('-') + | ||
'-compiless"'; | ||
res.setHeader('ETag', newETag); | ||
if (ifNoneMatch && ifNoneMatch.indexOf(newETag) !== -1) { | ||
return res.status(304).send(); | ||
} | ||
} | ||
if (ifNoneMatch && ifNoneMatch.indexOf(newETag) !== -1) { | ||
return res.status(304).send(); | ||
} | ||
} | ||
var cssText = importedFileNames.map(function (importedFileName) { | ||
return ".compilessinclude {background-image: url(" + Path.relative(baseDir, importedFileName) + "); display: none;}\n"; | ||
}).join("") + css; | ||
const cssText = | ||
importedFileNames | ||
.map( | ||
importedFileName => | ||
'.compilessinclude {background-image: url(' + | ||
Path.relative(baseDir, importedFileName) + | ||
'); display: none;}\n' | ||
) | ||
.join('') + css; | ||
res.setHeader('Content-Type', 'text/css; charset=utf-8'); | ||
res.setHeader('Content-Length', Buffer.byteLength(cssText)); | ||
res.end(cssText); | ||
})); | ||
} | ||
res.setHeader('Content-Type', 'text/css; charset=utf-8'); | ||
res.setHeader('Content-Length', Buffer.byteLength(cssText)); | ||
res.end(cssText); | ||
}) | ||
); | ||
} | ||
var chunks = []; | ||
res.on('data', function (chunk) { | ||
chunks.push(chunk); | ||
}).on('end', function () { | ||
var lessText = Buffer.concat(chunks).toString(); | ||
const chunks = []; | ||
res | ||
.on('data', chunk => { | ||
chunks.push(chunk); | ||
}) | ||
.on('end', () => { | ||
const lessText = Buffer.concat(chunks).toString(); | ||
if (isOldLessApi) { | ||
var parser = new less.Parser(lessOptions); | ||
parser.parse(lessText, passError(sendErrorResponse, function (root) { | ||
var css; | ||
try { | ||
css = root.toCSS(toCSSOptions); | ||
} catch (e) { | ||
return sendErrorResponse(e); | ||
} | ||
respondWithCss(css, parser.imports && parser.imports.files && Object.keys(parser.imports.files)); | ||
})); | ||
} else { | ||
less.render(lessText, lessOptions, passError(sendErrorResponse, function (output) { | ||
respondWithCss(output.css, output.imports); | ||
})); | ||
if (isOldLessApi) { | ||
const parser = new less.Parser(lessOptions); | ||
parser.parse( | ||
lessText, | ||
passError(sendErrorResponse, root => { | ||
let css; | ||
try { | ||
css = root.toCSS(toCSSOptions); | ||
} catch (e) { | ||
return sendErrorResponse(e); | ||
} | ||
}); | ||
}, {disableBackpressure: true}); | ||
} | ||
next(); | ||
}; | ||
respondWithCss( | ||
css, | ||
parser.imports && | ||
parser.imports.files && | ||
Object.keys(parser.imports.files) | ||
); | ||
}) | ||
); | ||
} else { | ||
less.render( | ||
lessText, | ||
lessOptions, | ||
passError(sendErrorResponse, output => { | ||
respondWithCss(output.css, output.imports); | ||
}) | ||
); | ||
} | ||
}); | ||
}, | ||
{ disableBackpressure: true } | ||
); | ||
} | ||
next(); | ||
}; | ||
}; |
{ | ||
"name": "express-compiless", | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"description": "Express middleware that compiles less files to css on the way out.", | ||
"main": "lib/compiless.js", | ||
"scripts": { | ||
"lint": "jshint .", | ||
"test": "mocha && npm run lint", | ||
"travis": "npm test && npm run coverage && <coverage/lcov.info coveralls", | ||
"coverage": "NODE_ENV=development istanbul cover _mocha -- --reporter dot && echo google-chrome coverage/lcov-report/index.html" | ||
"lint": "eslint . && prettier --check '**/*.js'", | ||
"test": "mocha", | ||
"ci": "npm test && npm run coverage && npm run lint && <coverage/lcov.info coveralls", | ||
"coverage": "NODE_ENV=development nyc --reporter=lcov --reporter=text --all -- npm test && echo google-chrome coverage/lcov-report/index.html" | ||
}, | ||
@@ -26,17 +26,30 @@ "repository": { | ||
"dependencies": { | ||
"async": "=2.6.1", | ||
"async": "^2.6.2", | ||
"csserror": "^2.0.2", | ||
"hijackresponse": "3.0.0", | ||
"less": "3.9.0", | ||
"passerror": "1.1.1" | ||
"hijackresponse": "^4.0.0", | ||
"less": "^3.9.0", | ||
"passerror": "^1.1.1" | ||
}, | ||
"devDependencies": { | ||
"coveralls": "^3.0.0", | ||
"express": "^4.16.3", | ||
"istanbul": "^0.4.5", | ||
"jshint": "^2.9.5", | ||
"mocha": "^5.0.5", | ||
"unexpected": "^10.37.2", | ||
"coveralls": "^3.0.3", | ||
"eslint": "^5.1.0", | ||
"eslint-config-prettier": "^4.0.0", | ||
"eslint-config-standard": "^12.0.0", | ||
"eslint-plugin-import": "^2.14.0", | ||
"eslint-plugin-mocha": "^5.2.1", | ||
"eslint-plugin-node": "^8.0.1", | ||
"eslint-plugin-promise": "^4.0.1", | ||
"eslint-plugin-standard": "^4.0.0", | ||
"express": "^4.16.4", | ||
"mocha": "^6.0.0", | ||
"nyc": "^13.3.0", | ||
"prettier": "^1.16.4", | ||
"unexpected": "^11.0.0", | ||
"unexpected-express": "^11.0.0" | ||
}, | ||
"nyc": { | ||
"include": [ | ||
"lib/**" | ||
] | ||
} | ||
} |
@@ -1,167 +0,170 @@ | ||
/*global describe, it, __dirname*/ | ||
var express = require('express'), | ||
Path = require('path'), | ||
unexpected = require('unexpected'), | ||
compiless = require('../lib/compiless'); | ||
/* global describe, it, __dirname */ | ||
const express = require('express'); | ||
const Path = require('path'); | ||
const unexpected = require('unexpected'); | ||
const compiless = require('../lib/compiless'); | ||
describe('compiless', function () { | ||
var root = Path.resolve(__dirname, 'root'), | ||
expect = unexpected.clone() | ||
.installPlugin(require('unexpected-express')) | ||
.addAssertion('to yield response', function (expect, subject, value) { | ||
return expect( | ||
express() | ||
.use(compiless({ root: root })) | ||
.use('/hello', function (req, res, next) { | ||
res.setHeader('Content-Type', 'text/plain'); | ||
res.setHeader('ETag', 'W/"fake-etag"'); | ||
res.status(200); | ||
res.write('world'); | ||
res.end(); | ||
}) | ||
.use(express['static'](root)), | ||
'to yield exchange', { | ||
request: subject, | ||
response: value | ||
} | ||
); | ||
}); | ||
describe('compiless', () => { | ||
const root = Path.resolve(__dirname, 'root'); | ||
const expect = unexpected | ||
.clone() | ||
.installPlugin(require('unexpected-express')) | ||
.addAssertion('to yield response', (expect, subject, value) => | ||
expect( | ||
express() | ||
.use(compiless({ root })) | ||
.use('/hello', (req, res, next) => { | ||
res.setHeader('Content-Type', 'text/plain'); | ||
res.setHeader('ETag', 'W/"fake-etag"'); | ||
res.status(200); | ||
res.write('world'); | ||
res.end(); | ||
}) | ||
.use(express['static'](root)), | ||
'to yield exchange', | ||
{ | ||
request: subject, | ||
response: value | ||
} | ||
) | ||
); | ||
it('should not mess with request for non-less file', function () { | ||
return expect('GET /something.txt', 'to yield response', { | ||
headers: { | ||
'Content-Type': 'text/plain; charset=UTF-8', | ||
'ETag': expect.it('not to match', /-compiless/), | ||
'Content-Length': '4' | ||
}, | ||
body: 'foo\n' | ||
}); | ||
}); | ||
it('should not mess with request for non-less file', () => | ||
expect('GET /something.txt', 'to yield response', { | ||
headers: { | ||
'Content-Type': 'text/plain; charset=UTF-8', | ||
ETag: expect.it('not to match', /-compiless/), | ||
'Content-Length': '4' | ||
}, | ||
body: 'foo\n' | ||
})); | ||
it('should not mess with request for non-less related route', function () { | ||
return expect('GET /hello', 'to yield response', { | ||
headers: { | ||
'Content-Type': 'text/plain', | ||
'ETag': expect.it('not to match', /-compiless/) | ||
}, | ||
body: 'world' | ||
}); | ||
}); | ||
it('should not mess with request for non-less related route', () => | ||
expect('GET /hello', 'to yield response', { | ||
headers: { | ||
'Content-Type': 'text/plain', | ||
ETag: expect.it('not to match', /-compiless/) | ||
}, | ||
body: 'world' | ||
})); | ||
it('should respond with an ETag header and support conditional GET', function () { | ||
return expect('GET /simple.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8', | ||
ETag: /^W\/".*-compiless"$/ | ||
}, | ||
body: '#foo #bar {\n color: blue;\n}\n' | ||
}).then(function (context) { | ||
var etag = context.httpResponse.headers.get('ETag'); | ||
return expect({ | ||
url: 'GET /simple.less', | ||
headers: { | ||
'If-None-Match': etag | ||
} | ||
}, 'to yield response', { | ||
statusCode: 304, | ||
headers: { | ||
ETag: etag | ||
} | ||
}); | ||
}); | ||
}); | ||
it('should respond with an ETag header and support conditional GET', () => | ||
expect('GET /simple.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8', | ||
ETag: /^W\/".*-compiless"$/ | ||
}, | ||
body: '#foo #bar {\n color: blue;\n}\n' | ||
}).then(context => { | ||
const etag = context.httpResponse.headers.get('ETag'); | ||
return expect( | ||
{ | ||
url: 'GET /simple.less', | ||
headers: { | ||
'If-None-Match': etag | ||
} | ||
}, | ||
'to yield response', | ||
{ | ||
statusCode: 304, | ||
headers: { | ||
ETag: etag | ||
} | ||
} | ||
); | ||
})); | ||
it('should not destroy if-none-match for non .less files', () => | ||
expect('GET /something.txt', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
ETag: /^W\/"[a-z0-9-]+"$/ | ||
}, | ||
body: 'foo\n' | ||
}).then(context => { | ||
const etag = context.httpResponse.headers.get('ETag'); | ||
return expect( | ||
{ | ||
url: 'GET /something.txt', | ||
headers: { | ||
'If-None-Match': etag | ||
} | ||
}, | ||
'to yield response', | ||
{ | ||
statusCode: 304, | ||
headers: { | ||
ETag: etag | ||
} | ||
} | ||
); | ||
})); | ||
it('should not destroy if-none-match for non .less files', function () { | ||
return expect('GET /something.txt', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
ETag: /^W\/"[a-z0-9-]+"$/ | ||
}, | ||
body: 'foo\n' | ||
}).then(function (context) { | ||
var etag = context.httpResponse.headers.get('ETag'); | ||
return expect({ | ||
url: 'GET /something.txt', | ||
headers: { | ||
'If-None-Match': etag | ||
} | ||
}, 'to yield response', { | ||
statusCode: 304, | ||
headers: { | ||
ETag: etag | ||
} | ||
}); | ||
}); | ||
}); | ||
it('should compile less file with @import to css with .compilessinclude rules first', () => | ||
expect('GET /stylesheet.less', 'to yield response', { | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: | ||
'.compilessinclude {background-image: url(imports/a.less); display: none;}\nbody {\n width: 100%;\n}\n#foo #bar {\n color: red;\n}\n/* multi-line\n comment\n*/\n' | ||
})); | ||
it('should compile less file with @import to css with .compilessinclude rules first', function () { | ||
return expect('GET /stylesheet.less', 'to yield response', { | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: '.compilessinclude {background-image: url(imports/a.less); display: none;}\nbody {\n width: 100%;\n}\n#foo #bar {\n color: red;\n}\n/* multi-line\n comment\n*/\n' | ||
}); | ||
}); | ||
it('should render less file that has a syntax error with the error message as the first thing in the output, wrapped in a body:before rule', () => | ||
expect('GET /syntaxerror.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling/ | ||
})); | ||
it('should render less file that has a syntax error with the error message as the first thing in the output, wrapped in a body:before rule', function () { | ||
return expect('GET /syntaxerror.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling/ | ||
}); | ||
}); | ||
it('should render less file that has an @import error with the error message as the first thing in the output, wrapped in a body:before rule', () => | ||
expect('GET /importerror.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling/ | ||
})); | ||
it('should render less file that has an @import error with the error message as the first thing in the output, wrapped in a body:before rule', function () { | ||
return expect('GET /importerror.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling/ | ||
}); | ||
}); | ||
it('should render less file that has an @import that points at a file with a syntax error', () => | ||
expect('GET /importedsyntaxerror.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling/ | ||
})); | ||
it('should render less file that has an @import that points at a file with a syntax error', function () { | ||
return expect('GET /importedsyntaxerror.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling/ | ||
}); | ||
}); | ||
it('should render less file that has an second level @import error with the error message as the first thing in the output, wrapped in a body:before rule', () => | ||
expect('GET /secondlevelimporterror.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling/ | ||
})); | ||
it('should render less file that has an second level @import error with the error message as the first thing in the output, wrapped in a body:before rule', function () { | ||
return expect('GET /secondlevelimporterror.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling/ | ||
}); | ||
}); | ||
it('should rewrite urls correctly', () => | ||
expect( | ||
'GET /importLessWithRelativeImageReferenceInDifferentDir.less', | ||
'to yield response', | ||
{ | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /url\(imports\/images\/foo.png\)/ | ||
} | ||
)); | ||
it('should rewrite urls correctly', function () { | ||
return expect('GET /importLessWithRelativeImageReferenceInDifferentDir.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /url\(imports\/images\/foo.png\)/ | ||
}); | ||
}); | ||
it('should deliver a response even though the less file has @imports and references an undefined variable', function () { | ||
return expect('GET /undefinedVariable.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling.*variable.*undefined/ | ||
}); | ||
}); | ||
it('should deliver a response even though the less file has @imports and references an undefined variable', () => | ||
expect('GET /undefinedVariable.less', 'to yield response', { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'text/css; charset=utf-8' | ||
}, | ||
body: /Error compiling.*variable.*undefined/ | ||
})); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23
354
18831
15
1
+ Addedasync@2.6.4(transitive)
+ Addedcopy-anything@2.0.6(transitive)
+ Addedhijackresponse@4.0.1(transitive)
+ Addedis-what@3.14.1(transitive)
+ Addedless@3.13.1(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedmake-dir@2.1.0(transitive)
+ Addednative-request@1.1.2(transitive)
+ Addedpify@4.0.1(transitive)
+ Addedsemver@5.7.2(transitive)
+ Addedtslib@1.14.1(transitive)
- Removedajv@6.12.6(transitive)
- Removedasap@2.0.6(transitive)
- Removedasn1@0.2.6(transitive)
- Removedassert-plus@1.0.0(transitive)
- Removedasynckit@0.4.0(transitive)
- Removedaws-sign2@0.7.0(transitive)
- Removedaws4@1.13.2(transitive)
- Removedbcrypt-pbkdf@1.0.2(transitive)
- Removedcaseless@0.12.0(transitive)
- Removedclone@2.1.2(transitive)
- Removedcombined-stream@1.0.8(transitive)
- Removedcore-util-is@1.0.2(transitive)
- Removeddashdash@1.14.1(transitive)
- Removeddelayed-stream@1.0.0(transitive)
- Removedecc-jsbn@0.1.2(transitive)
- Removedextend@3.0.2(transitive)
- Removedextsprintf@1.3.0(transitive)
- Removedfast-deep-equal@3.1.3(transitive)
- Removedfast-json-stable-stringify@2.1.0(transitive)
- Removedforever-agent@0.6.1(transitive)
- Removedform-data@2.3.3(transitive)
- Removedgetpass@0.1.7(transitive)
- Removedhar-schema@2.0.0(transitive)
- Removedhar-validator@5.1.5(transitive)
- Removedhijackresponse@3.0.0(transitive)
- Removedhttp-signature@1.2.0(transitive)
- Removedis-typedarray@1.0.0(transitive)
- Removedisstream@0.1.2(transitive)
- Removedjsbn@0.1.1(transitive)
- Removedjson-schema@0.4.0(transitive)
- Removedjson-schema-traverse@0.4.1(transitive)
- Removedjson-stringify-safe@5.0.1(transitive)
- Removedjsprim@1.4.2(transitive)
- Removedless@3.9.0(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removedminimist@1.2.8(transitive)
- Removedmkdirp@0.5.6(transitive)
- Removedoauth-sign@0.9.0(transitive)
- Removedperformance-now@2.1.0(transitive)
- Removedpromise@7.3.1(transitive)
- Removedpsl@1.10.0(transitive)
- Removedpunycode@2.3.1(transitive)
- Removedqs@6.5.3(transitive)
- Removedrequest@2.88.2(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedsshpk@1.18.0(transitive)
- Removedtough-cookie@2.5.0(transitive)
- Removedtunnel-agent@0.6.0(transitive)
- Removedtweetnacl@0.14.5(transitive)
- Removeduri-js@4.4.1(transitive)
- Removeduuid@3.4.0(transitive)
- Removedverror@1.10.0(transitive)
Updatedasync@^2.6.2
Updatedhijackresponse@^4.0.0
Updatedless@^3.9.0
Updatedpasserror@^1.1.1