Files
vscode-eftl-language/.agents/skills/verify-eftl-linter/scripts/check-repository.mjs
T

118 lines
3.9 KiB
JavaScript

#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import { createRequire } from 'node:module';
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';
const args = process.argv.slice(2);
const includeLint = args.includes('--lint');
const positional = args.filter(arg => !arg.startsWith('--'));
const root = resolve(positional[0] ?? process.cwd());
const require = createRequire(import.meta.url);
function fail(message) {
throw new Error(message);
}
function readJson(relativePath) {
const absolutePath = resolve(root, relativePath);
if (!existsSync(absolutePath)) {
fail(`Missing ${relativePath}`);
}
try {
return JSON.parse(readFileSync(absolutePath, 'utf8'));
} catch (error) {
fail(`Invalid JSON in ${relativePath}: ${error.message}`);
}
}
function runNpm(script) {
const windows = process.platform === 'win32';
const command = windows ? (process.env.ComSpec ?? 'cmd.exe') : 'npm';
const commandArgs = windows ? ['/d', '/s', '/c', `npm run ${script}`] : ['run', script];
const result = spawnSync(command, commandArgs, {
cwd: root,
stdio: 'inherit'
});
if (result.error) {
fail(`Could not run npm script ${script}: ${result.error.message}`);
}
if (result.status !== 0) {
fail(`npm run ${script} exited with ${result.status}`);
}
}
function assertContributionPaths(packageJson) {
const paths = [packageJson.main];
for (const language of packageJson.contributes?.languages ?? []) {
paths.push(language.configuration);
}
for (const grammar of packageJson.contributes?.grammars ?? []) {
paths.push(grammar.path);
}
for (const snippet of packageJson.contributes?.snippets ?? []) {
paths.push(snippet.path);
}
for (const relativePath of paths.filter(Boolean)) {
if (!existsSync(resolve(root, relativePath))) {
fail(`package.json references missing path ${relativePath}`);
}
}
}
function smokeTestCore() {
const tokenizerPath = resolve(root, 'out/tokenizer.js');
const parserPath = resolve(root, 'out/parser.js');
if (!existsSync(tokenizerPath) || !existsSync(parserPath)) {
fail('Compiled tokenizer/parser output is missing');
}
const { EftlTokenizer } = require(tokenizerPath);
const { EftlParser } = require(parserPath);
const sample = readFileSync(resolve(root, 'examples/sample.eftl'), 'utf8');
const sampleResult = new EftlTokenizer(sample).tokenize();
const sampleParse = new EftlParser(sampleResult.tokens).parse();
if (sampleResult.errors.length || sampleParse.errors.length) {
fail(`sample.eftl is not accepted: ${sampleResult.errors.length} tokenizer and ${sampleParse.errors.length} parser errors`);
}
const mismatched = new EftlTokenizer('[EFTL][IF][/EFTL]').tokenize();
if (new EftlParser(mismatched.tokens).parse().errors.length === 0) {
fail('Parser smoke case did not reject mismatched nesting');
}
const comment = new EftlTokenizer('[EFTL][!-- unfinished').tokenize();
if (comment.errors.length === 0) {
fail('Tokenizer smoke case did not reject an unterminated comment');
}
}
try {
const packageJson = readJson('package.json');
readJson('language-configuration.json');
readJson('syntaxes/eftl.tmLanguage.json');
readJson('snippets/eftl.json');
runNpm('compile');
assertContributionPaths(packageJson);
smokeTestCore();
if (packageJson.scripts?.test) {
runNpm('test');
} else {
console.warn('WARN: package.json has no test script; only smoke cases were run.');
}
if (includeLint) {
runNpm('lint');
} else if (packageJson.scripts?.lint) {
console.warn('WARN: lint was skipped; pass --lint after configuring ESLint.');
}
console.log('EFTL repository baseline checks passed.');
} catch (error) {
console.error(`FAIL: ${error.message}`);
process.exitCode = 1;
}