Skip to content

Commit

Permalink
Add --working-directory option.
Browse files Browse the repository at this point in the history
This refactors all read/write operations to be relative to the specified
`--working-directory`. This working directory is threaded through the
various internal/private API methods for configuration, and
`process.cwd()` is now **only** used to default the working directory
(when the command line option is not specified).
  • Loading branch information
rwjblue committed Oct 7, 2020
1 parent 1722f4f commit 324b7b8
Show file tree
Hide file tree
Showing 11 changed files with 359 additions and 167 deletions.
46 changes: 31 additions & 15 deletions bin/ember-template-lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const readFile = promisify(fs.readFile);

const STDIN = '/dev/stdin';

async function buildLinterOptions(filePath, filename = '', isReadingStdin) {
async function buildLinterOptions(workingDir, filePath, filename = '', isReadingStdin) {
if (isReadingStdin) {
let filePath = filename;
let moduleId = filePath.slice(0, -4);
Expand All @@ -30,7 +30,8 @@ async function buildLinterOptions(filePath, filename = '', isReadingStdin) {
return { source, filePath, moduleId };
} else {
let moduleId = filePath.slice(0, -4);
let source = await readFile(path.resolve(filePath), { encoding: 'utf8' });
let resolvedFilePath = path.resolve(workingDir, filePath);
let source = await readFile(resolvedFilePath, { encoding: 'utf8' });

return { source, filePath, moduleId };
}
Expand All @@ -49,21 +50,20 @@ function lintSource(linter, options, shouldFix) {
}
}

function executeGlobby(pattern, ignore) {
return (
globby
// `--no-ignore-pattern` results in `ignorePattern === [false]`
.sync(pattern, ignore[0] === false ? {} : { ignore, gitignore: true })
.filter((filePath) => filePath.slice(-4) === '.hbs')
);
function executeGlobby(workingDir, pattern, ignore) {
// `--no-ignore-pattern` results in `ignorePattern === [false]`
let options =
ignore[0] === false ? { cwd: workingDir } : { cwd: workingDir, gitignore: true, ignore };

return globby.sync(pattern, options).filter((filePath) => filePath.slice(-4) === '.hbs');
}

function expandFileGlobs(filePatterns, ignorePattern, glob = executeGlobby) {
function expandFileGlobs(workingDir, filePatterns, ignorePattern, glob = executeGlobby) {
let result = new Set();

filePatterns.forEach((pattern) => {
let isHBS = pattern.slice(-4) === '.hbs';
let isLiteralPath = !isGlob(pattern) && fs.existsSync(pattern);
let isLiteralPath = !isGlob(pattern) && fs.existsSync(path.resolve(workingDir, pattern));

if (isHBS && isLiteralPath) {
let isIgnored = micromatch.isMatch(pattern, ignorePattern);
Expand All @@ -75,19 +75,19 @@ function expandFileGlobs(filePatterns, ignorePattern, glob = executeGlobby) {
return;
}

glob(pattern, ignorePattern).forEach((filePath) => result.add(filePath));
glob(workingDir, pattern, ignorePattern).forEach((filePath) => result.add(filePath));
});

return result;
}

function getFilesToLint(filePatterns, ignorePattern = []) {
function getFilesToLint(workingDir, filePatterns, ignorePattern = []) {
let files;

if (filePatterns.length === 0 || filePatterns.includes('-') || filePatterns.includes(STDIN)) {
files = new Set([STDIN]);
} else {
files = expandFileGlobs(filePatterns, ignorePattern);
files = expandFileGlobs(workingDir, filePatterns, ignorePattern);
}

return files;
Expand Down Expand Up @@ -134,6 +134,15 @@ function parseArgv(_argv) {
describe: 'Output errors with source description',
boolean: true,
},
'working-directory': {
alias: 'cwd',
describe: 'Path to a directory that should be considered as the current working directory.',
type: 'string',
// defaulting to `.` here to refer to `process.cwd()`, setting the default to `process.cwd()` itself
// would make our snapshots unstable (and make the help output unaligned since most directory paths
// are fairly deep)
default: '.',
},
'no-config-path': {
describe:
'Does not use the local template-lintrc, will use a blank template-lintrc instead',
Expand Down Expand Up @@ -165,6 +174,11 @@ function parseArgv(_argv) {
parser.exit(1);
} else {
let options = parser.parse(_argv);

if (options.workingDirectory === '.') {
options.workingDirectory = process.cwd();
}

return options;
}
}
Expand Down Expand Up @@ -221,6 +235,7 @@ async function run() {
let linter;
try {
linter = new Linter({
workingDir: options.workingDirectory,
configPath: options.configPath,
config,
rule: options.rule,
Expand All @@ -232,11 +247,12 @@ async function run() {
return;
}

let filePaths = getFilesToLint(positional, options.ignorePattern);
let filePaths = getFilesToLint(options.workingDirectory, positional, options.ignorePattern);

let resultsAccumulator = [];
for (let relativeFilePath of filePaths) {
let linterOptions = await buildLinterOptions(
options.workingDirectory,
relativeFilePath,
options.filename,
filePaths.has(STDIN)
Expand Down
14 changes: 4 additions & 10 deletions lib/-private/module-status-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ const path = require('path');
const micromatch = require('micromatch');

class ModuleStatusCache {
constructor(config, configPath) {
constructor(workingDir, config, configPath) {
this.workingDir = workingDir;
this.config = config;
this.configPath = configPath || '';
this.cache = {
Expand All @@ -12,13 +13,6 @@ class ModuleStatusCache {
};
}

get processCWD() {
if (!this._processCWD) {
this._processCWD = process.cwd();
}
return this._processCWD;
}

lookupPending(moduleId) {
if (!moduleId || !this.config.pending) {
return false;
Expand All @@ -27,7 +21,7 @@ class ModuleStatusCache {
this.cache.pendingLookup = this._extractPendingCache();
}
if (!(moduleId in this.cache.pending)) {
const fullPathModuleId = path.resolve(this.processCWD, moduleId);
const fullPathModuleId = path.resolve(this.workingDir, moduleId);
this.cache.pending[moduleId] = this.cache.pendingLookup[fullPathModuleId];
}
return this.cache.pending[moduleId];
Expand Down Expand Up @@ -64,7 +58,7 @@ class ModuleStatusCache {

resolveFullModuleId(moduleId) {
if (!this._baseDirBasedOnConfigPath) {
this._baseDirBasedOnConfigPath = path.resolve(this.processCWD, path.dirname(this.configPath));
this._baseDirBasedOnConfigPath = path.resolve(this.workingDir, path.dirname(this.configPath));
}
return path.resolve(this._baseDirBasedOnConfigPath, moduleId);
}
Expand Down
39 changes: 21 additions & 18 deletions lib/get-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ const ALLOWED_ERROR_CODES = new Set([
'QUALIFIED_PATH_RESOLUTION_FAILED',
]);

function requirePlugin(pluginName, fromConfigPath) {
let basedir = fromConfigPath === undefined ? process.cwd() : path.dirname(fromConfigPath);
function requirePlugin(workingDir, pluginName, fromConfigPath) {
let basedir = fromConfigPath === undefined ? workingDir : path.dirname(fromConfigPath);

// throws exception if not found
let pluginPath = resolve.sync(pluginName, { basedir });

return require(pluginPath); // eslint-disable-line import/no-dynamic-require
}

function resolveProjectConfig(options) {
function resolveProjectConfig(workingDir, options) {
let configPath;

if (options.configPath) {
configPath = path.resolve(process.cwd(), options.configPath);
configPath = path.resolve(workingDir, options.configPath);

try {
// Making sure that the filePath exists, before requiring it directly this is
Expand Down Expand Up @@ -152,7 +152,7 @@ function migrateRulesFromRoot(config, source, options) {
}
}

function processPlugins(plugins = [], options, checkForCircularReference) {
function processPlugins(workingDir, plugins = [], options, checkForCircularReference) {
let logger = options.console || console;

let pluginsHash = {};
Expand All @@ -165,7 +165,7 @@ function processPlugins(plugins = [], options, checkForCircularReference) {
// the second argument here should actually be the config file path for
// the _currently being processed_ config file (not neccesarily the one
// specified to the bin script)
plugin = requirePlugin(pluginName, options.resolvedConfigPath);
plugin = requirePlugin(workingDir, pluginName, options.resolvedConfigPath);
}

let errorMessage;
Expand All @@ -192,13 +192,16 @@ function processPlugins(plugins = [], options, checkForCircularReference) {

forEachPluginConfiguration(pluginsHash, (configuration) => {
// process plugins recursively
Object.assign(pluginsHash, processPlugins(configuration.plugins, options, pluginsHash));
Object.assign(
pluginsHash,
processPlugins(workingDir, configuration.plugins, options, pluginsHash)
);
});

return pluginsHash;
}

function processLoadedRules(config, options) {
function processLoadedRules(workingDir, config, options) {
let loadedRules;
if (config.loadedRules) {
loadedRules = config.loadedRules;
Expand All @@ -216,15 +219,15 @@ function processLoadedRules(config, options) {
}

forEachPluginConfiguration(config.plugins, (configuration) => {
let plugins = processPlugins(configuration.plugins, options, config.plugins);
let plugins = processPlugins(workingDir, configuration.plugins, options, config.plugins);
// process plugins recursively
processLoadedRules({ plugins, loadedRules });
processLoadedRules(workingDir, { plugins, loadedRules });
});

return loadedRules;
}

function processLoadedConfigurations(config, options) {
function processLoadedConfigurations(workingDir, config, options) {
let loadedConfigurations;
if (config.loadedConfigurations) {
loadedConfigurations = config.loadedConfigurations;
Expand All @@ -238,8 +241,8 @@ function processLoadedConfigurations(config, options) {
loadedConfigurations[name] = configuration;

// load plugins recursively
let plugins = processPlugins(configuration.plugins, options, config.plugins);
processLoadedConfigurations({ plugins, loadedConfigurations }, options);
let plugins = processPlugins(workingDir, configuration.plugins, options, config.plugins);
processLoadedConfigurations(workingDir, { plugins, loadedConfigurations }, options);
});

return loadedConfigurations;
Expand Down Expand Up @@ -420,8 +423,8 @@ function processRules(config) {
return processedRules;
}

function getProjectConfig(options) {
let source = options.config || resolveProjectConfig(options);
function getProjectConfig(workingDir, options) {
let source = options.config || resolveProjectConfig(workingDir, options);
let config;

if (source._processed) {
Expand All @@ -433,9 +436,9 @@ function getProjectConfig(options) {
ensureRootProperties(config, source);
migrateRulesFromRoot(config, source, options);

config.plugins = processPlugins(source.plugins, options);
config.loadedRules = processLoadedRules(config, options);
config.loadedConfigurations = processLoadedConfigurations(config, options);
config.plugins = processPlugins(workingDir, source.plugins, options);
config.loadedRules = processLoadedRules(workingDir, config, options);
config.loadedConfigurations = processLoadedConfigurations(workingDir, config, options);
processExtends(config, options);
processIgnores(config);

Expand Down
5 changes: 3 additions & 2 deletions lib/get-editor-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const path = require('path');
const micromatch = require('micromatch');

class EditorConfigResolver {
constructor() {
constructor(workingDir) {
this.workingDir = workingDir;
this.EDITOR_CONFIG_CACHE = null;
this.CONFIG_RESOLUTIONS_CACHE = new Map();
/**
Expand Down Expand Up @@ -208,7 +209,7 @@ class EditorConfigResolver {
dirname(/root/path/this.file.does.not.exist) => /root/path
*/
resolveEditorConfigFiles(
_filepath = path.join(process.cwd(), 'this.file.does.not.exist'),
_filepath = path.join(this.workingDir, 'this.file.does.not.exist'),
_options = {}
) {
const [resolvedFilePath, processedOptions] = this.opts(_filepath, _options);
Expand Down
11 changes: 8 additions & 3 deletions lib/linter.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,17 @@ class Linter {

this.options = options;
this.console = options.console || console;
this.workingDir = options.workingDir || process.cwd();

this.loadConfig();

this.constructor = Linter;
this.editorConfigResolver = new EditorConfigResolver();
this.editorConfigResolver = new EditorConfigResolver(this.workingDir);
this.editorConfigResolver.resolveEditorConfigFiles();
}

loadConfig() {
this.config = getProjectConfig(this.options);
this.config = getProjectConfig(this.workingDir, this.options);

// we were passed a rule, add the rule being passed in, to the config.
// ex:
Expand All @@ -56,7 +57,11 @@ class Linter {

this.config.rules[name] = config;
}
this._moduleStatusCache = new ModuleStatusCache(this.config, this.options.configPath);
this._moduleStatusCache = new ModuleStatusCache(
this.workingDir,
this.config,
this.options.configPath
);
}

/**
Expand Down
Loading

0 comments on commit 324b7b8

Please sign in to comment.