mirror of
https://github.com/github/codeql-action.git
synced 2026-01-01 20:20:07 +08:00
202 lines
5.9 KiB
JavaScript
202 lines
5.9 KiB
JavaScript
'use strict';
|
||
let isMainThread = true;
|
||
let supportsWorkers = false;
|
||
try {
|
||
({isMainThread} = require('worker_threads'));
|
||
supportsWorkers = true;
|
||
} catch {}
|
||
|
||
const {classify, hasExtension, isHelperish, matches, normalizeFileForMatching, normalizeGlobs, normalizePatterns} = require('./lib/globs');
|
||
|
||
let resolveGlobs;
|
||
let resolveGlobsSync;
|
||
|
||
if (!supportsWorkers || !isMainThread) {
|
||
const normalizeExtensions = require('./lib/extensions');
|
||
const {loadConfig, loadConfigSync} = require('./lib/load-config');
|
||
const providerManager = require('./lib/provider-manager');
|
||
|
||
const configCache = new Map();
|
||
|
||
const collectProviders = ({conf, projectDir}) => {
|
||
const providers = [];
|
||
if (Reflect.has(conf, 'babel')) {
|
||
const {level, main} = providerManager.babel(projectDir);
|
||
providers.push({
|
||
level,
|
||
main: main({config: conf.babel}),
|
||
type: 'babel'
|
||
});
|
||
}
|
||
|
||
if (Reflect.has(conf, 'typescript')) {
|
||
const {level, main} = providerManager.typescript(projectDir);
|
||
providers.push({
|
||
level,
|
||
main: main({config: conf.typescript}),
|
||
type: 'typescript'
|
||
});
|
||
}
|
||
|
||
return providers;
|
||
};
|
||
|
||
const buildGlobs = ({conf, providers, projectDir, overrideExtensions, overrideFiles}) => {
|
||
const extensions = overrideExtensions ?
|
||
normalizeExtensions(overrideExtensions) :
|
||
normalizeExtensions(conf.extensions, providers);
|
||
|
||
return {
|
||
cwd: projectDir,
|
||
...normalizeGlobs({
|
||
extensions,
|
||
files: overrideFiles ? overrideFiles : conf.files,
|
||
providers
|
||
})
|
||
};
|
||
};
|
||
|
||
resolveGlobsSync = (projectDir, overrideExtensions, overrideFiles) => {
|
||
if (!configCache.has(projectDir)) {
|
||
const conf = loadConfigSync({resolveFrom: projectDir});
|
||
const providers = collectProviders({conf, projectDir});
|
||
configCache.set(projectDir, {conf, providers});
|
||
}
|
||
|
||
const {conf, providers} = configCache.get(projectDir);
|
||
return buildGlobs({conf, providers, projectDir, overrideExtensions, overrideFiles});
|
||
};
|
||
|
||
resolveGlobs = async (projectDir, overrideExtensions, overrideFiles) => {
|
||
if (!configCache.has(projectDir)) {
|
||
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(conf => { // eslint-disable-line promise/prefer-await-to-then
|
||
const providers = collectProviders({conf, projectDir});
|
||
return {conf, providers};
|
||
}));
|
||
}
|
||
|
||
const {conf, providers} = await configCache.get(projectDir);
|
||
return buildGlobs({conf, providers, projectDir, overrideExtensions, overrideFiles});
|
||
};
|
||
}
|
||
|
||
if (supportsWorkers) {
|
||
const v8 = require('v8');
|
||
|
||
const MAX_DATA_LENGTH_EXCLUSIVE = 100 * 1024; // Allocate 100 KiB to exchange globs.
|
||
|
||
if (isMainThread) {
|
||
const {Worker} = require('worker_threads');
|
||
let data;
|
||
let sync;
|
||
let worker;
|
||
|
||
resolveGlobsSync = (projectDir, overrideExtensions, overrideFiles) => {
|
||
if (worker === undefined) {
|
||
const dataBuffer = new SharedArrayBuffer(MAX_DATA_LENGTH_EXCLUSIVE);
|
||
data = new Uint8Array(dataBuffer);
|
||
|
||
const syncBuffer = new SharedArrayBuffer(4);
|
||
sync = new Int32Array(syncBuffer);
|
||
|
||
worker = new Worker(__filename, {
|
||
workerData: {
|
||
dataBuffer,
|
||
syncBuffer,
|
||
firstMessage: {projectDir, overrideExtensions, overrideFiles}
|
||
}
|
||
});
|
||
worker.unref();
|
||
} else {
|
||
worker.postMessage({projectDir, overrideExtensions, overrideFiles});
|
||
}
|
||
|
||
Atomics.wait(sync, 0, 0);
|
||
|
||
const byteLength = Atomics.exchange(sync, 0, 0);
|
||
if (byteLength === MAX_DATA_LENGTH_EXCLUSIVE) {
|
||
throw new Error('Globs are over 100 KiB and cannot be resolved');
|
||
}
|
||
|
||
const globsOrError = v8.deserialize(data.slice(0, byteLength));
|
||
if (globsOrError instanceof Error) {
|
||
throw globsOrError;
|
||
}
|
||
|
||
return globsOrError;
|
||
};
|
||
} else {
|
||
const {parentPort, workerData} = require('worker_threads');
|
||
const data = new Uint8Array(workerData.dataBuffer);
|
||
const sync = new Int32Array(workerData.syncBuffer);
|
||
|
||
const handleMessage = async ({projectDir, overrideExtensions, overrideFiles}) => {
|
||
let encoded;
|
||
try {
|
||
const globs = await resolveGlobs(projectDir, overrideExtensions, overrideFiles);
|
||
encoded = v8.serialize(globs);
|
||
} catch (error) {
|
||
encoded = v8.serialize(error);
|
||
}
|
||
|
||
const byteLength = encoded.length < MAX_DATA_LENGTH_EXCLUSIVE ? encoded.copy(data) : MAX_DATA_LENGTH_EXCLUSIVE;
|
||
Atomics.store(sync, 0, byteLength);
|
||
Atomics.notify(sync, 0);
|
||
};
|
||
|
||
parentPort.on('message', handleMessage);
|
||
handleMessage(workerData.firstMessage);
|
||
delete workerData.firstMessage;
|
||
}
|
||
}
|
||
|
||
const helperCache = new Map();
|
||
|
||
function load(projectDir, overrides) {
|
||
const cacheKey = `${JSON.stringify(overrides)}\n${projectDir}`;
|
||
if (helperCache.has(cacheKey)) {
|
||
return helperCache.get(cacheKey);
|
||
}
|
||
|
||
let helperPatterns = [];
|
||
if (overrides && overrides.helpers !== undefined) {
|
||
if (!Array.isArray(overrides.helpers) || overrides.helpers.length === 0) {
|
||
throw new Error('The ’helpers’ override must be an array containing glob patterns.');
|
||
}
|
||
|
||
helperPatterns = normalizePatterns(overrides.helpers);
|
||
}
|
||
|
||
const globs = resolveGlobsSync(projectDir, overrides && overrides.extensions, overrides && overrides.files);
|
||
|
||
const classifyForESLint = file => {
|
||
const {isTest} = classify(file, globs);
|
||
let isHelper = false;
|
||
if (!isTest && hasExtension(globs.extensions, file)) {
|
||
file = normalizeFileForMatching(projectDir, file);
|
||
isHelper = isHelperish(file) || (helperPatterns.length > 0 && matches(file, helperPatterns));
|
||
}
|
||
|
||
return {isHelper, isTest};
|
||
};
|
||
|
||
const helper = Object.freeze({
|
||
classifyFile: classifyForESLint,
|
||
classifyImport: importPath => {
|
||
if (hasExtension(globs.extensions, importPath)) {
|
||
// The importPath has one of the test file extensions: we can classify
|
||
// it directly.
|
||
return classifyForESLint(importPath);
|
||
}
|
||
|
||
// Add the first extension. If multiple extensions are available, assume
|
||
// patterns are not biased to any particular extension.
|
||
return classifyForESLint(`${importPath}.${globs.extensions[0]}`);
|
||
}
|
||
});
|
||
helperCache.set(cacheKey, helper);
|
||
return helper;
|
||
}
|
||
|
||
exports.load = load;
|