the-forest/client/node_modules/clean-css/lib/reader/read-sources.js
2024-09-17 20:35:18 -04:00

345 lines
13 KiB
JavaScript

var fs = require('fs');
var path = require('path');
var applySourceMaps = require('./apply-source-maps');
var extractImportUrlAndMedia = require('./extract-import-url-and-media');
var isAllowedResource = require('./is-allowed-resource');
var loadOriginalSources = require('./load-original-sources');
var normalizePath = require('./normalize-path');
var rebase = require('./rebase');
var rebaseLocalMap = require('./rebase-local-map');
var rebaseRemoteMap = require('./rebase-remote-map');
var restoreImport = require('./restore-import');
var tokenize = require('../tokenizer/tokenize');
var Token = require('../tokenizer/token');
var Marker = require('../tokenizer/marker');
var hasProtocol = require('../utils/has-protocol');
var isImport = require('../utils/is-import');
var isRemoteResource = require('../utils/is-remote-resource');
var UNKNOWN_URI = 'uri:unknown';
var FILE_RESOURCE_PROTOCOL = 'file://';
function readSources(input, context, callback) {
return doReadSources(input, context, function(tokens) {
return applySourceMaps(tokens, context, function() {
return loadOriginalSources(context, function() { return callback(tokens); });
});
});
}
function doReadSources(input, context, callback) {
if (typeof input == 'string') {
return fromString(input, context, callback);
} if (Buffer.isBuffer(input)) {
return fromString(input.toString(), context, callback);
} if (Array.isArray(input)) {
return fromArray(input, context, callback);
} if (typeof input == 'object') {
return fromHash(input, context, callback);
}
}
function fromString(input, context, callback) {
context.source = undefined;
context.sourcesContent[undefined] = input;
context.stats.originalSize += input.length;
return fromStyles(input, context, { inline: context.options.inline }, callback);
}
function fromArray(input, context, callback) {
var inputAsImports = input.reduce(function(accumulator, uriOrHash) {
if (typeof uriOrHash === 'string') {
return addStringSource(uriOrHash, accumulator);
}
return addHashSource(uriOrHash, context, accumulator);
}, []);
return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback);
}
function fromHash(input, context, callback) {
var inputAsImports = addHashSource(input, context, []);
return fromStyles(inputAsImports.join(''), context, { inline: ['all'] }, callback);
}
function addStringSource(input, imports) {
imports.push(restoreAsImport(normalizeUri(input)));
return imports;
}
function addHashSource(input, context, imports) {
var uri;
var normalizedUri;
var source;
for (uri in input) {
source = input[uri];
normalizedUri = normalizeUri(uri);
imports.push(restoreAsImport(normalizedUri));
context.sourcesContent[normalizedUri] = source.styles;
if (source.sourceMap) {
trackSourceMap(source.sourceMap, normalizedUri, context);
}
}
return imports;
}
function normalizeUri(uri) {
var currentPath = path.resolve('');
var absoluteUri;
var relativeToCurrentPath;
var normalizedUri;
if (isRemoteResource(uri)) {
return uri;
}
absoluteUri = path.isAbsolute(uri)
? uri
: path.resolve(uri);
relativeToCurrentPath = path.relative(currentPath, absoluteUri);
normalizedUri = normalizePath(relativeToCurrentPath);
return normalizedUri;
}
function trackSourceMap(sourceMap, uri, context) {
var parsedMap = typeof sourceMap == 'string'
? JSON.parse(sourceMap)
: sourceMap;
var rebasedMap = isRemoteResource(uri)
? rebaseRemoteMap(parsedMap, uri)
: rebaseLocalMap(parsedMap, uri || UNKNOWN_URI, context.options.rebaseTo);
context.inputSourceMapTracker.track(uri, rebasedMap);
}
function restoreAsImport(uri) {
return restoreImport('url(' + uri + ')', '') + Marker.SEMICOLON;
}
function fromStyles(styles, context, parentInlinerContext, callback) {
var tokens;
var rebaseConfig = {};
if (!context.source) {
rebaseConfig.fromBase = path.resolve('');
rebaseConfig.toBase = context.options.rebaseTo;
} else if (isRemoteResource(context.source)) {
rebaseConfig.fromBase = context.source;
rebaseConfig.toBase = context.source;
} else if (path.isAbsolute(context.source)) {
rebaseConfig.fromBase = path.dirname(context.source);
rebaseConfig.toBase = context.options.rebaseTo;
} else {
rebaseConfig.fromBase = path.dirname(path.resolve(context.source));
rebaseConfig.toBase = context.options.rebaseTo;
}
tokens = tokenize(styles, context);
tokens = rebase(tokens, context.options.rebase, context.validator, rebaseConfig);
return allowsAnyImports(parentInlinerContext.inline)
? inline(tokens, context, parentInlinerContext, callback)
: callback(tokens);
}
function allowsAnyImports(inline) {
return !(inline.length == 1 && inline[0] == 'none');
}
function inline(tokens, externalContext, parentInlinerContext, callback) {
var inlinerContext = {
afterContent: false,
callback: callback,
errors: externalContext.errors,
externalContext: externalContext,
fetch: externalContext.options.fetch,
inlinedStylesheets: parentInlinerContext.inlinedStylesheets || externalContext.inlinedStylesheets,
inline: parentInlinerContext.inline,
inlineRequest: externalContext.options.inlineRequest,
inlineTimeout: externalContext.options.inlineTimeout,
isRemote: parentInlinerContext.isRemote || false,
localOnly: externalContext.localOnly,
outputTokens: [],
rebaseTo: externalContext.options.rebaseTo,
sourceTokens: tokens,
warnings: externalContext.warnings
};
return doInlineImports(inlinerContext);
}
function doInlineImports(inlinerContext) {
var token;
var i, l;
for (i = 0, l = inlinerContext.sourceTokens.length; i < l; i++) {
token = inlinerContext.sourceTokens[i];
if (token[0] == Token.AT_RULE && isImport(token[1])) {
inlinerContext.sourceTokens.splice(0, i);
return inlineStylesheet(token, inlinerContext);
} if (token[0] == Token.AT_RULE || token[0] == Token.COMMENT) {
inlinerContext.outputTokens.push(token);
} else {
inlinerContext.outputTokens.push(token);
inlinerContext.afterContent = true;
}
}
inlinerContext.sourceTokens = [];
return inlinerContext.callback(inlinerContext.outputTokens);
}
function inlineStylesheet(token, inlinerContext) {
var uriAndMediaQuery = extractImportUrlAndMedia(token[1]);
var uri = uriAndMediaQuery[0];
var mediaQuery = uriAndMediaQuery[1];
var metadata = token[2];
return isRemoteResource(uri)
? inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext)
: inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext);
}
function inlineRemoteStylesheet(uri, mediaQuery, metadata, inlinerContext) {
var isAllowed = isAllowedResource(uri, true, inlinerContext.inline);
var originalUri = uri;
var isLoaded = uri in inlinerContext.externalContext.sourcesContent;
var isRuntimeResource = !hasProtocol(uri);
if (inlinerContext.inlinedStylesheets.indexOf(uri) > -1) {
inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as it has already been imported.');
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
} if (inlinerContext.localOnly && inlinerContext.afterContent) {
inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as no callback given and after other content.');
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
} if (isRuntimeResource) {
inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as no protocol given.');
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
} if (inlinerContext.localOnly && !isLoaded) {
inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as no callback given.');
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
} if (!isAllowed && inlinerContext.afterContent) {
inlinerContext.warnings.push('Ignoring remote @import of "' + uri + '" as resource is not allowed and after other content.');
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
} if (!isAllowed) {
inlinerContext.warnings.push('Skipping remote @import of "' + uri + '" as resource is not allowed.');
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
}
inlinerContext.inlinedStylesheets.push(uri);
function whenLoaded(error, importedStyles) {
if (error) {
inlinerContext.errors.push('Broken @import declaration of "' + uri + '" - ' + error);
return process.nextTick(function() {
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
doInlineImports(inlinerContext);
});
}
inlinerContext.inline = inlinerContext.externalContext.options.inline;
inlinerContext.isRemote = true;
inlinerContext.externalContext.source = originalUri;
inlinerContext.externalContext.sourcesContent[uri] = importedStyles;
inlinerContext.externalContext.stats.originalSize += importedStyles.length;
return fromStyles(importedStyles, inlinerContext.externalContext, inlinerContext, function(importedTokens) {
importedTokens = wrapInMedia(importedTokens, mediaQuery, metadata);
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens);
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
});
}
return isLoaded
? whenLoaded(null, inlinerContext.externalContext.sourcesContent[uri])
: inlinerContext.fetch(uri, inlinerContext.inlineRequest, inlinerContext.inlineTimeout, whenLoaded);
}
function inlineLocalStylesheet(uri, mediaQuery, metadata, inlinerContext) {
var protocolLessUri = uri.replace(FILE_RESOURCE_PROTOCOL, '');
var currentPath = path.resolve('');
var absoluteUri = path.isAbsolute(protocolLessUri)
? path.resolve(currentPath, protocolLessUri[0] == '/' ? protocolLessUri.substring(1) : protocolLessUri)
: path.resolve(inlinerContext.rebaseTo, protocolLessUri);
var relativeToCurrentPath = path.relative(currentPath, absoluteUri);
var importedStyles;
var isAllowed = isAllowedResource(protocolLessUri, false, inlinerContext.inline);
var normalizedPath = normalizePath(relativeToCurrentPath);
var isLoaded = normalizedPath in inlinerContext.externalContext.sourcesContent;
if (inlinerContext.inlinedStylesheets.indexOf(absoluteUri) > -1) {
inlinerContext.warnings.push('Ignoring local @import of "' + protocolLessUri + '" as it has already been imported.');
} else if (isAllowed && !isLoaded && (!fs.existsSync(absoluteUri) || !fs.statSync(absoluteUri).isFile())) {
inlinerContext.errors.push('Ignoring local @import of "' + protocolLessUri + '" as resource is missing.');
} else if (!isAllowed && inlinerContext.afterContent) {
inlinerContext.warnings.push('Ignoring local @import of "' + protocolLessUri + '" as resource is not allowed and after other content.');
} else if (inlinerContext.afterContent) {
inlinerContext.warnings.push('Ignoring local @import of "' + protocolLessUri + '" as after other content.');
} else if (!isAllowed) {
inlinerContext.warnings.push('Skipping local @import of "' + protocolLessUri + '" as resource is not allowed.');
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(inlinerContext.sourceTokens.slice(0, 1));
} else {
importedStyles = isLoaded
? inlinerContext.externalContext.sourcesContent[normalizedPath]
: fs.readFileSync(absoluteUri, 'utf-8');
if (importedStyles.charCodeAt(0) === 65279) {
importedStyles = importedStyles.substring(1);
}
inlinerContext.inlinedStylesheets.push(absoluteUri);
inlinerContext.inline = inlinerContext.externalContext.options.inline;
inlinerContext.externalContext.source = normalizedPath;
inlinerContext.externalContext.sourcesContent[normalizedPath] = importedStyles;
inlinerContext.externalContext.stats.originalSize += importedStyles.length;
return fromStyles(importedStyles, inlinerContext.externalContext, inlinerContext, function(importedTokens) {
importedTokens = wrapInMedia(importedTokens, mediaQuery, metadata);
inlinerContext.outputTokens = inlinerContext.outputTokens.concat(importedTokens);
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
});
}
inlinerContext.sourceTokens = inlinerContext.sourceTokens.slice(1);
return doInlineImports(inlinerContext);
}
function wrapInMedia(tokens, mediaQuery, metadata) {
if (mediaQuery) {
return [[Token.NESTED_BLOCK, [[Token.NESTED_BLOCK_SCOPE, '@media ' + mediaQuery, metadata]], tokens]];
}
return tokens;
}
module.exports = readSources;