Skip to content

Commit c664d72

Browse files
committed
Reorganize code into ConfigCache.ts and Debug.ts
1 parent 93d4431 commit c664d72

File tree

3 files changed

+148
-112
lines changed

3 files changed

+148
-112
lines changed

eslint-plugin/src/ConfigCache.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
2+
import * as path from 'path';
3+
4+
import { Debug } from './Debug';
5+
6+
interface ICachedConfig {
7+
loadTimeMs: number;
8+
lastCheckTimeMs: number;
9+
configFile: TSDocConfigFile;
10+
}
11+
12+
// How often to check for modified input files. If a file's modification timestamp has changed, then we will
13+
// evict the cache entry immediately.
14+
const CACHE_CHECK_INTERVAL_MS: number = 3 * 1000;
15+
16+
// Evict old entries from the cache after this much time, regardless of whether the file was detected as being
17+
// modified or not.
18+
const CACHE_EXPIRE_MS: number = 20 * 1000;
19+
20+
// If this many objects accumulate in the cache, then it is cleared to avoid a memory leak.
21+
const CACHE_MAX_SIZE: number = 100;
22+
23+
export class ConfigCache {
24+
// findConfigPathForFolder() result --> loaded tsdoc.json configuration
25+
private static _cachedConfigs: Map<string, ICachedConfig> = new Map<string, ICachedConfig>();
26+
27+
/**
28+
* Node.js equivalent of performance.now().
29+
*/
30+
private static _getTimeInMs(): number {
31+
throw new Error('oops');
32+
const [seconds, nanoseconds] = process.hrtime();
33+
return seconds * 1000 + nanoseconds / 1000000;
34+
}
35+
36+
public static getForSourceFile(sourceFilePath: string): TSDocConfigFile {
37+
// First, determine the file to be loaded. If not found, the configFilePath will be an empty string.
38+
const configFilePath: string = TSDocConfigFile.findConfigPathForFolder(sourceFilePath);
39+
40+
// If configFilePath is an empty string, then we'll use the folder of sourceFilePath as our cache key
41+
// (instead of an empty string)
42+
const cacheKey: string = configFilePath || path.dirname(sourceFilePath);
43+
Debug.log(`Cache key: "${cacheKey}"`);
44+
45+
const nowMs: number = ConfigCache._getTimeInMs();
46+
47+
let cachedConfig: ICachedConfig | undefined = undefined;
48+
49+
// Do we have a cached object?
50+
cachedConfig = ConfigCache._cachedConfigs.get(cacheKey);
51+
52+
if (cachedConfig) {
53+
Debug.log('Cache hit');
54+
55+
// Is the cached object still valid?
56+
const loadAgeMs: number = nowMs - cachedConfig.loadTimeMs;
57+
const lastCheckAgeMs: number = nowMs - cachedConfig.lastCheckTimeMs;
58+
59+
if (loadAgeMs > CACHE_EXPIRE_MS || loadAgeMs < 0) {
60+
Debug.log('Evicting because item is expired');
61+
cachedConfig = undefined;
62+
ConfigCache._cachedConfigs.delete(cacheKey);
63+
} else if (lastCheckAgeMs > CACHE_CHECK_INTERVAL_MS || lastCheckAgeMs < 0) {
64+
Debug.log('Checking for modifications');
65+
cachedConfig.lastCheckTimeMs = nowMs;
66+
if (cachedConfig.configFile.checkForModifiedFiles()) {
67+
// Invalidate the cache because it failed to load completely
68+
Debug.log('Evicting because item was modified');
69+
cachedConfig = undefined;
70+
ConfigCache._cachedConfigs.delete(cacheKey);
71+
}
72+
}
73+
}
74+
75+
// Load the object
76+
if (!cachedConfig) {
77+
if (ConfigCache._cachedConfigs.size > CACHE_MAX_SIZE) {
78+
Debug.log('Clearing cache');
79+
ConfigCache._cachedConfigs.clear(); // avoid a memory leak
80+
}
81+
82+
const configFile: TSDocConfigFile = TSDocConfigFile.loadFile(configFilePath);
83+
84+
if (configFile.fileNotFound) {
85+
Debug.log(`File not found: "${configFilePath}"`);
86+
} else {
87+
Debug.log(`Loaded: "${configFilePath}"`);
88+
}
89+
90+
cachedConfig = {
91+
configFile,
92+
lastCheckTimeMs: nowMs,
93+
loadTimeMs: nowMs
94+
};
95+
96+
ConfigCache._cachedConfigs.set(cacheKey, cachedConfig);
97+
}
98+
99+
return cachedConfig.configFile;
100+
}
101+
}

eslint-plugin/src/Debug.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
export class Debug {
3+
// To debug the plugin, temporarily uncomment the body of this function
4+
public static log(message: string): void {
5+
// message = require("process").pid + ": " + message;
6+
// console.log(message);
7+
// require('fs').writeFileSync('C:\\Git\\log.txt', message + '\r\n', { flag: 'as' });
8+
}
9+
}

eslint-plugin/src/index.ts

Lines changed: 38 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
1+
import * as eslint from "eslint";
2+
import * as ESTree from "estree";
23
import {
34
TSDocParser,
45
TextRange,
56
TSDocConfiguration,
67
ParserContext
78
} from "@microsoft/tsdoc";
8-
import {
9-
TSDocConfigFile
10-
} from '@microsoft/tsdoc-config';
11-
import * as eslint from "eslint";
12-
import * as ESTree from "estree";
13-
import * as path from 'path';
9+
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
10+
11+
import { Debug } from './Debug';
12+
import { ConfigCache } from './ConfigCache';
1413

1514
const tsdocMessageIds: {[x: string]: string} = {};
1615

@@ -23,33 +22,6 @@ interface IPlugin {
2322
rules: {[x: string]: eslint.Rule.RuleModule};
2423
}
2524

26-
// To debug the plugin, temporarily uncomment the body of this function
27-
function debug(message: string): void {
28-
message = require("process").pid + ": " + message;
29-
console.log(message);
30-
require('fs').writeFileSync('C:\\Git\\log.txt', message + '\r\n', { flag: 'as' });
31-
}
32-
33-
interface ICachedConfig {
34-
loadTimeMs: number;
35-
lastCheckTimeMs: number;
36-
configFile: TSDocConfigFile;
37-
}
38-
39-
// How often to check for modified input files. If a file's modification timestamp has changed, then we will
40-
// evict the cache entry immediately.
41-
const CACHE_CHECK_INTERVAL_MS: number = 15*1000;
42-
43-
// Evict old entries from the cache after this much time, regardless of whether the file was detected as being
44-
// modified or not.
45-
const CACHE_EXPIRE_MS: number = 30*1000;
46-
47-
// If this many objects accumulate in the cache, then it is cleared to avoid a memory leak.
48-
const CACHE_MAX_SIZE: number = 100;
49-
50-
// findConfigPathForFolder() result --> loaded tsdoc.json configuration
51-
const cachedConfigs: Map<string, ICachedConfig> = new Map<string, ICachedConfig>();
52-
5325
const plugin: IPlugin = {
5426
rules: {
5527
// NOTE: The actual ESLint rule name will be "tsdoc/syntax". It is calculated by deleting "eslint-plugin-"
@@ -72,89 +44,43 @@ const plugin: IPlugin = {
7244
},
7345
create: (context: eslint.Rule.RuleContext) => {
7446
const sourceFilePath: string = context.getFilename();
47+
Debug.log(`Linting: "${sourceFilePath}"`);
7548

76-
debug(`Linting: "${sourceFilePath}"`);
77-
78-
// First, determine the file to be loaded. If not found, the configFilePath will be an empty string.
79-
const configFilePath: string ='';// TSDocConfigFile.findConfigPathForFolder(sourceFilePath);
80-
81-
// If configFilePath is an empty string, then we'll use the folder of sourceFilePath as our cache key
82-
// (instead of an empty string)
83-
const cacheKey: string = configFilePath || path.dirname(sourceFilePath);
84-
debug(`Cache key: "${cacheKey}"`);
85-
86-
const nowMs: number = (new Date()).getTime();
87-
88-
let cachedConfig: ICachedConfig | undefined = undefined;
89-
90-
// Do we have a cached object?
91-
cachedConfig = cachedConfigs.get(cacheKey);
92-
93-
if (cachedConfig) {
94-
debug('Cache hit');
95-
96-
// Is the cached object still valid?
97-
const loadAgeMs: number = nowMs - cachedConfig.loadTimeMs;
98-
const lastCheckAgeMs: number = nowMs - cachedConfig.lastCheckTimeMs;
99-
100-
if (loadAgeMs > CACHE_EXPIRE_MS || loadAgeMs < 0) {
101-
debug('Evicting because item is expired');
102-
cachedConfig = undefined;
103-
cachedConfigs.delete(cacheKey);
104-
} else if (lastCheckAgeMs > CACHE_CHECK_INTERVAL_MS || lastCheckAgeMs < 0) {
105-
debug('Checking for modifications');
106-
cachedConfig.lastCheckTimeMs = nowMs;
107-
if (cachedConfig.configFile.checkForModifiedFiles()) {
108-
// Invalidate the cache because it failed to load completely
109-
debug('Evicting because item was modified');
110-
cachedConfig = undefined;
111-
cachedConfigs.delete(cacheKey);
112-
}
113-
}
114-
}
115-
116-
// Load the object
117-
if (!cachedConfig) {
118-
if (cachedConfigs.size > CACHE_MAX_SIZE) {
119-
debug('Clearing cache');
120-
cachedConfigs.clear(); // avoid a memory leak
121-
}
122-
123-
debug(`LOADING CONFIG: ${configFilePath}`);
124-
cachedConfig = {
125-
configFile: TSDocConfigFile.loadFile(configFilePath),
126-
lastCheckTimeMs: nowMs,
127-
loadTimeMs: nowMs
128-
};
129-
130-
cachedConfigs.set(cacheKey, cachedConfig);
131-
}
132-
133-
const tsdocConfigFile: TSDocConfigFile = cachedConfig.configFile;
13449
const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration()
13550

136-
if (!tsdocConfigFile.fileNotFound) {
137-
if (tsdocConfigFile.hasErrors) {
138-
context.report({
139-
loc: { line: 1, column: 1 },
140-
messageId: "error-loading-config-file",
141-
data: {
142-
details: tsdocConfigFile.getErrorSummary()
143-
}
144-
});
145-
}
51+
try {
52+
const tsdocConfigFile: TSDocConfigFile = ConfigCache.getForSourceFile(sourceFilePath);
53+
if (!tsdocConfigFile.fileNotFound) {
54+
if (tsdocConfigFile.hasErrors) {
55+
context.report({
56+
loc: { line: 1, column: 1 },
57+
messageId: "error-loading-config-file",
58+
data: {
59+
details: tsdocConfigFile.getErrorSummary()
60+
}
61+
});
62+
}
14663

147-
try {
148-
tsdocConfigFile.configureParser(tsdocConfiguration);
149-
} catch (e) {
150-
context.report({
151-
loc: { line: 1, column: 1 },
152-
messageId: "error-applying-config",
153-
data: {
154-
details: e.message
155-
}
156-
});
64+
try {
65+
tsdocConfigFile.configureParser(tsdocConfiguration);
66+
} catch (e) {
67+
context.report({
68+
loc: { line: 1, column: 1 },
69+
messageId: "error-applying-config",
70+
data: {
71+
details: e.message
72+
}
73+
});
74+
}
15775
}
76+
} catch (e) {
77+
context.report({
78+
loc: { line: 1, column: 1 },
79+
messageId: "error-loading-config-file",
80+
data: {
81+
details: `Unexpected exception: ${e.message}`
82+
}
83+
});
15884
}
15985

16086
const tsdocParser: TSDocParser = new TSDocParser(tsdocConfiguration);

0 commit comments

Comments
 (0)