chore(misc): add custom lint rule for ensuring pnpm-lock.yaml version (#28010)
This commit is contained in:
parent
53e11788f1
commit
2e33230753
@ -64,6 +64,16 @@
|
||||
],
|
||||
"@nx/workspace/valid-command-object": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["pnpm-lock.yaml"],
|
||||
"parser": "./tools/eslint-rules/raw-file-parser.js",
|
||||
"rules": {
|
||||
"@nx/workspace/ensure-pnpm-lock-version": [
|
||||
"error",
|
||||
{ "version": "9.0" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
9
nx.json
9
nx.json
@ -123,7 +123,14 @@
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"dependsOn": ["build-native", "^build-native"]
|
||||
"dependsOn": [
|
||||
"build-native",
|
||||
"^build-native",
|
||||
"@nx/nx-source:lint-pnpm-lock"
|
||||
]
|
||||
},
|
||||
"lint-pnpm-lock": {
|
||||
"cache": true
|
||||
},
|
||||
"e2e": {
|
||||
"cache": true,
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"test": "nx run-many -t test",
|
||||
"e2e": "nx run-many -t e2e --projects ./e2e/*",
|
||||
"build:wasm": "rustup override set nightly-2024-07-19 && rustup target add wasm32-wasip1-threads && WASI_SDK_PATH=\"$(pwd)/wasi-sdk-23.0-x86_64-linux\" CMAKE_BUILD_PARALLEL_LEVEL=2 LIBSQLITE3_FLAGS=\"-DLONGDOUBLE_TYPE=double\" pnpm exec nx run-many -t build-native-wasm && rustup override unset"
|
||||
"build:wasm": "rustup override set nightly-2024-07-19 && rustup target add wasm32-wasip1-threads && WASI_SDK_PATH=\"$(pwd)/wasi-sdk-23.0-x86_64-linux\" CMAKE_BUILD_PARALLEL_LEVEL=2 LIBSQLITE3_FLAGS=\"-DLONGDOUBLE_TYPE=double\" pnpm exec nx run-many -t build-native-wasm && rustup override unset",
|
||||
"lint-pnpm-lock": "eslint pnpm-lock.yaml"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
@ -377,6 +378,7 @@
|
||||
},
|
||||
"nx": {
|
||||
"includedScripts": [
|
||||
"lint-pnpm-lock",
|
||||
"echo",
|
||||
"check-commit",
|
||||
"check-format",
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
import {
|
||||
RULE_NAME as ensurePnpmLockVersionName,
|
||||
rule as ensurePnpmLockVersion,
|
||||
} from './rules/ensure-pnpm-lock-version';
|
||||
import {
|
||||
RULE_NAME as validCommandObjectName,
|
||||
rule as validCommandObject,
|
||||
@ -34,5 +38,6 @@ module.exports = {
|
||||
rules: {
|
||||
[validSchemaDescriptionName]: validSchemaDescription,
|
||||
[validCommandObjectName]: validCommandObject,
|
||||
[ensurePnpmLockVersionName]: ensurePnpmLockVersion,
|
||||
},
|
||||
};
|
||||
|
||||
23
tools/eslint-rules/raw-file-parser.js
Normal file
23
tools/eslint-rules/raw-file-parser.js
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* We have a custom lint rule for our pnpm-lock.yaml file and naturally ESLint does not natively know how to parse it.
|
||||
* Rather than using a full yaml parser for this one case (which will need to spend time creating a real AST for the giant
|
||||
* lock file), we can instead use a custom parser which just immediately returns a dummy AST and then build the reading of
|
||||
* the lock file into the rule itself.
|
||||
*/
|
||||
module.exports = {
|
||||
parseForESLint: (code) => ({
|
||||
ast: {
|
||||
type: 'Program',
|
||||
loc: { start: 0, end: code.length },
|
||||
range: [0, code.length],
|
||||
body: [],
|
||||
comments: [],
|
||||
tokens: [],
|
||||
},
|
||||
services: { isPlain: true },
|
||||
scopeManager: null,
|
||||
visitorKeys: {
|
||||
Program: [],
|
||||
},
|
||||
}),
|
||||
};
|
||||
109
tools/eslint-rules/rules/ensure-pnpm-lock-version.ts
Normal file
109
tools/eslint-rules/rules/ensure-pnpm-lock-version.ts
Normal file
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* This file sets you up with structure needed for an ESLint rule.
|
||||
*
|
||||
* It leverages utilities from @typescript-eslint to allow TypeScript to
|
||||
* provide autocompletions etc for the configuration.
|
||||
*
|
||||
* Your rule's custom logic will live within the create() method below
|
||||
* and you can learn more about writing ESLint rules on the official guide:
|
||||
*
|
||||
* https://eslint.org/docs/developer-guide/working-with-rules
|
||||
*
|
||||
* You can also view many examples of existing rules here:
|
||||
*
|
||||
* https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/src/rules
|
||||
*/
|
||||
|
||||
import { ESLintUtils } from '@typescript-eslint/utils';
|
||||
import { closeSync, openSync, readSync } from 'node:fs';
|
||||
|
||||
// NOTE: The rule will be available in ESLint configs as "@nx/workspace-ensure-pnpm-lock-version"
|
||||
export const RULE_NAME = 'ensure-pnpm-lock-version';
|
||||
|
||||
export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
||||
name: RULE_NAME,
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: ``,
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
version: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
messages: {
|
||||
unparseableLockfileVersion:
|
||||
'Could not parse lockfile version from pnpm-lock.yaml, the file may be corrupted or the ensure-pnpm-lock-version lint rule may need to be updated.',
|
||||
incorrectLockfileVersion:
|
||||
'pnpm-lock.yaml has a lockfileVersion of {{version}}, but {{expectedVersion}} is required.',
|
||||
},
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
// Read upon creation of the rule, the contents should not change during linting
|
||||
const lockfileFirstLine = readFirstLineSync('pnpm-lock.yaml');
|
||||
// Extract the version number, it will be a string in single quotes
|
||||
const lockfileVersion = lockfileFirstLine.match(
|
||||
/lockfileVersion:\s*'([^']+)'/
|
||||
)?.[1];
|
||||
|
||||
const options = context.options as { version: string }[];
|
||||
if (!Array.isArray(options) || options.length === 0) {
|
||||
throw new Error('Expected an array of options with a version property');
|
||||
}
|
||||
const expectedLockfileVersion = options[0].version;
|
||||
return {
|
||||
Program(node) {
|
||||
if (!lockfileVersion) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'unparseableLockfileVersion',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (lockfileVersion !== expectedLockfileVersion) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'incorrectLockfileVersion',
|
||||
data: {
|
||||
version: lockfileVersion,
|
||||
expectedVersion: expectedLockfileVersion,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* pnpm-lock.yaml is a huge file, so only read the first line as efficiently as possible
|
||||
* for optimum linting performance.
|
||||
*/
|
||||
function readFirstLineSync(filePath: string) {
|
||||
const BUFFER_SIZE = 64; // Optimized for the expected line length
|
||||
const buffer = Buffer.alloc(BUFFER_SIZE);
|
||||
let line = '';
|
||||
let bytesRead: number;
|
||||
let fd: number;
|
||||
try {
|
||||
fd = openSync(filePath, 'r');
|
||||
bytesRead = readSync(fd, buffer, 0, BUFFER_SIZE, 0);
|
||||
line = buffer.toString('utf8', 0, bytesRead).split('\n')[0];
|
||||
} catch (err) {
|
||||
throw err; // Re-throw to allow caller to handle
|
||||
} finally {
|
||||
if (fd !== undefined) {
|
||||
closeSync(fd);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user