CasperSecurity
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = setupTrackingContext;
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _fastGlob = _interopRequireDefault(require("fast-glob"));
var _quickLru = _interopRequireDefault(require("quick-lru"));
var _normalizePath = _interopRequireDefault(require("normalize-path"));
var _hashConfig = _interopRequireDefault(require("../util/hashConfig"));
var _getModuleDependencies = _interopRequireDefault(require("../lib/getModuleDependencies"));
var _resolveConfig = _interopRequireDefault(require("../public/resolve-config"));
var _resolveConfigPath = _interopRequireDefault(require("../util/resolveConfigPath"));
var _sharedState = require("./sharedState");
var _setupContextUtils = require("./setupContextUtils");
var _parseDependency = _interopRequireDefault(require("../util/parseDependency"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
let configPathCache = new _quickLru.default({
maxSize: 100
});
let candidateFilesCache = new WeakMap();
function getCandidateFiles(context, tailwindConfig) {
if (candidateFilesCache.has(context)) {
return candidateFilesCache.get(context);
}
let candidateFiles = tailwindConfig.content.files.filter((item)=>typeof item === 'string'
).map((contentPath)=>(0, _normalizePath).default(contentPath)
);
return candidateFilesCache.set(context, candidateFiles).get(context);
}
// Get the config object based on a path
function getTailwindConfig(configOrPath) {
let userConfigPath = (0, _resolveConfigPath).default(configOrPath);
if (userConfigPath !== null) {
let [prevConfig, prevConfigHash, prevDeps, prevModified] = configPathCache.get(userConfigPath) || [];
let newDeps = (0, _getModuleDependencies).default(userConfigPath).map((dep)=>dep.file
);
let modified = false;
let newModified = new Map();
for (let file of newDeps){
let time = _fs.default.statSync(file).mtimeMs;
newModified.set(file, time);
if (!prevModified || !prevModified.has(file) || time > prevModified.get(file)) {
modified = true;
}
}
// It hasn't changed (based on timestamps)
if (!modified) {
return [
prevConfig,
userConfigPath,
prevConfigHash,
prevDeps
];
}
// It has changed (based on timestamps), or first run
for (let file1 of newDeps){
delete require.cache[file1];
}
let newConfig = (0, _resolveConfig).default(require(userConfigPath));
let newHash = (0, _hashConfig).default(newConfig);
configPathCache.set(userConfigPath, [
newConfig,
newHash,
newDeps,
newModified
]);
return [
newConfig,
userConfigPath,
newHash,
newDeps
];
}
// It's a plain object, not a path
let newConfig = (0, _resolveConfig).default(configOrPath.config === undefined ? configOrPath : configOrPath.config);
return [
newConfig,
null,
(0, _hashConfig).default(newConfig),
[]
];
}
function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
let changedContent = context.tailwindConfig.content.files.filter((item)=>typeof item.raw === 'string'
).map(({ raw , extension ='html' })=>({
content: raw,
extension
})
);
for (let changedFile of resolveChangedFiles(candidateFiles, fileModifiedMap)){
let content = _fs.default.readFileSync(changedFile, 'utf8');
let extension = _path.default.extname(changedFile).slice(1);
changedContent.push({
content,
extension
});
}
return changedContent;
}
function resolveChangedFiles(candidateFiles, fileModifiedMap) {
let changedFiles = new Set();
_sharedState.env.DEBUG && console.time('Finding changed files');
let files = _fastGlob.default.sync(candidateFiles);
for (let file of files){
let prevModified = fileModifiedMap.has(file) ? fileModifiedMap.get(file) : -Infinity;
let modified = _fs.default.statSync(file).mtimeMs;
if (modified > prevModified) {
changedFiles.add(file);
fileModifiedMap.set(file, modified);
}
}
_sharedState.env.DEBUG && console.timeEnd('Finding changed files');
return changedFiles;
}
function setupTrackingContext(configOrPath) {
return ({ tailwindDirectives , registerDependency , applyDirectives })=>{
return (root, result)=>{
let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] = getTailwindConfig(configOrPath);
let contextDependencies = new Set(configDependencies);
// If there are no @tailwind or @apply rules, we don't consider this CSS
// file or its dependencies to be dependencies of the context. Can reuse
// the context even if they change. We may want to think about `@layer`
// being part of this trigger too, but it's tough because it's impossible
// for a layer in one file to end up in the actual @tailwind rule in
// another file since independent sources are effectively isolated.
if (tailwindDirectives.size > 0 || applyDirectives.size > 0) {
// Add current css file as a context dependencies.
contextDependencies.add(result.opts.from);
// Add all css @import dependencies as context dependencies.
for (let message of result.messages){
if (message.type === 'dependency') {
contextDependencies.add(message.file);
}
}
}
let [context] = (0, _setupContextUtils).getContext(root, result, tailwindConfig, userConfigPath, tailwindConfigHash, contextDependencies);
let candidateFiles = getCandidateFiles(context, tailwindConfig);
// If there are no @tailwind or @apply rules, we don't consider this CSS file or it's
// dependencies to be dependencies of the context. Can reuse the context even if they change.
// We may want to think about `@layer` being part of this trigger too, but it's tough
// because it's impossible for a layer in one file to end up in the actual @tailwind rule
// in another file since independent sources are effectively isolated.
if (tailwindDirectives.size > 0 || applyDirectives.size > 0) {
let fileModifiedMap = (0, _setupContextUtils).getFileModifiedMap(context);
// Add template paths as postcss dependencies.
for (let fileOrGlob of candidateFiles){
let dependency = (0, _parseDependency).default(fileOrGlob);
if (dependency) {
registerDependency(dependency);
}
}
for (let changedContent of resolvedChangedContent(context, candidateFiles, fileModifiedMap)){
context.changedContent.push(changedContent);
}
}
for (let file of configDependencies){
registerDependency({
type: 'dependency',
file
});
}
return context;
};
};
}