Add lint rules generator
MODIFY
packages/@romejs/diagnostics/categories.ts
packages/@romejs/diagnostics/categories.ts
// all category names are defined. This allows the naming scheme to be more easily reviewed and
// made consistent.
export type DiagnosticCategory =
+ | LintDiagnosticCategory
| 'analyzeDependencies/cjsExportInES'
| 'args/fileNotFound'
| 'args/invalid'
@@ -20,54 +21,7 @@ export type DiagnosticCategory =
| 'format/disabled'
| 'internalError/httpServer'
| 'internalError/request'
- | 'lint/camelCase'
- | 'lint/caseSingleStatement'
- | 'lint/defaultExportSameBasename'
- | 'lint/disabled'
- | 'lint/doubleEquals'
- | 'lint/duplicateImportSource'
- | 'lint/emptyBlocks'
- | 'lint/getterReturn'
- | 'lint/inconsiderateLanguage'
- | 'lint/noArguments'
- | 'lint/noAsyncPromiseExecutor'
- | 'lint/noCatchAssign'
- | 'lint/noCompareNegZero'
- | 'lint/noCondAssign'
- | 'lint/noDebugger'
- | 'lint/noDelete'
- | 'lint/noDeleteVars'
- | 'lint/noDupeArgs'
- | 'lint/noDuplicateCase'
- | 'lint/noDuplicateGroupNamesInRegularExpressions'
- | 'lint/noDuplicateKeys'
- | 'lint/noEmptyCharacterClass'
- | 'lint/noExplicitAny'
- | 'lint/noExtraBooleanCast'
- | 'lint/noFunctionAssign'
- | 'lint/noImportAssign'
- | 'lint/noLabelVar'
- | 'lint/noMultipleSpacesInRegularExpressionLiterals'
- | 'lint/noReferenceToNonExistingGroup'
- | 'lint/noSetterReturn'
- | 'lint/noShadowRestrictedNames'
- | 'lint/noShorthandArrayType'
- | 'lint/noTemplateCurlyInString'
- | 'lint/noUnsafeFinally'
- | 'lint/noVar'
| 'lint/pendingFixes'
- | 'lint/preferBlockStatements'
- | 'lint/preferFunctionDeclarations'
- | 'lint/preferTemplate'
- | 'lint/preferWhile'
- | 'lint/restrictedGlobals'
- | 'lint/singleVarDeclarator'
- | 'lint/sortImportExportSpecifiers'
- | 'lint/sparseArray'
- | 'lint/undeclaredVariables'
- | 'lint/unsafeNegation'
- | 'lint/unusedVariables'
- | 'lint/react/jsxNoCommentText'
| 'lsp/parse'
| 'parse/js'
| 'parse/json'
@@ -132,3 +86,53 @@ export type DiagnosticCategoryPrefix =
| 'tests'
| 'typeCheck'
| 'v8';
+
+// EVERYTHING BELOW IS AUTOGENERATED. SEE SCRIPTS FOLDER FOR UPDATE SCRIPTS
+
+type LintDiagnosticCategory =
+ | 'lint/camelCase'
+ | 'lint/caseSingleStatement'
+ | 'lint/defaultExportSameBasename'
+ | 'lint/doubleEquals'
+ | 'lint/duplicateImportSource'
+ | 'lint/emptyBlocks'
+ | 'lint/getterReturn'
+ | 'lint/inconsiderateLanguage'
+ | 'lint/jsxNoCommentText'
+ | 'lint/noArguments'
+ | 'lint/noAsyncPromiseExecutor'
+ | 'lint/noCatchAssign'
+ | 'lint/noCompareNegZero'
+ | 'lint/noCondAssign'
+ | 'lint/noDebugger'
+ | 'lint/noDelete'
+ | 'lint/noDeleteVars'
+ | 'lint/noDupeArgs'
+ | 'lint/noDuplicateCase'
+ | 'lint/noDuplicateGroupNamesInRegularExpressions'
+ | 'lint/noDuplicateKeys'
+ | 'lint/noEmptyCharacterClass'
+ | 'lint/noExplicitAny'
+ | 'lint/noExtraBooleanCast'
+ | 'lint/noFunctionAssign'
+ | 'lint/noImportAssign'
+ | 'lint/noLabelVar'
+ | 'lint/noMultipleSpacesInRegularExpressionLiterals'
+ | 'lint/noReferenceToNonExistingGroup'
+ | 'lint/noSetterReturn'
+ | 'lint/noShadowRestrictedNames'
+ | 'lint/noShorthandArrayType'
+ | 'lint/noTemplateCurlyInString'
+ | 'lint/noUnsafeFinally'
+ | 'lint/noVar'
+ | 'lint/preferBlockStatements'
+ | 'lint/preferFunctionDeclarations'
+ | 'lint/preferTemplate'
+ | 'lint/preferWhile'
+ | 'lint/restrictedGlobals'
+ | 'lint/singleVarDeclarator'
+ | 'lint/sortImportExportSpecifiers'
+ | 'lint/sparseArray'
+ | 'lint/undeclaredVariables'
+ | 'lint/unsafeNegation'
+ | 'lint/unusedVariables';

MODIFY
packages/@romejs/diagnostics/descriptions.ts
packages/@romejs/diagnostics/descriptions.ts
@@ -351,7 +351,7 @@ export const descriptions = createMessages({
message: 'A while loop should be used over a for loop',
},
REACT_JSX_NO_COMMENT_TEXT: {
- category: 'lint/react/jsxNoCommentText',
+ category: 'lint/jsxNoCommentText',
message: 'Comments inside children should be placed in braces',
},
UNSAFE_NEGATION: {

MODIFY
scripts/_constants.cjs
scripts/_constants.cjs
@@ -21,5 +21,21 @@ exports.analysisFolder = path.join(
'js-analysis',
'evaluators',
);
+exports.lintRulesFolder = path.join(
+ exports.packages,
+ 'js-compiler',
+ 'lint',
+ 'rules',
+);
+exports.categoriesFile = path.join(
+ exports.packages,
+ 'diagnostics',
+ 'categories.ts',
+);
+exports.descriptionsFile = path.join(
+ exports.packages,
+ 'diagnostics',
+ 'descriptions.ts',
+);
exports.astFolder = path.join(exports.packages, 'js-ast');
exports.argv = process.argv.slice(2);

MODIFY
scripts/assert-generated
scripts/assert-generated
@@ -15,6 +15,7 @@ const {execDev, writtenFiles} = require('./_utils.cjs');
// Generate files
require('./runtime-modules/update.cjs');
require('./ast/update.cjs');
+require('./lint/update.cjs');
// Autofix them
execDev(['lint', ...writtenFiles, '--save']);

MODIFY
scripts/ast/add.cjs
scripts/ast/add.cjs
@@ -21,7 +21,7 @@ const {write, getBuilderName} = require('../_utils.cjs');
const nodeType = process.argv[2];
const category = process.argv[3];
if (nodeType === undefined || category === undefined) {
- console.error('node add-new-ast-type.js [node-type] [category]');
+ console.error('node scripts/ast/add.cjs [node-type] [category]');
process.exit(1);
}

ADD
scripts/lint/add.cjs
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+require('../_setup.cjs');
+
+const {write} = require('../_utils.cjs');
+const {lintRulesFolder, descriptionsFile} = require('../_constants.cjs');
+const path = require('path');
+const fs = require('fs');
+
+const ruleName = process.argv[2];
+const category = process.argv[3];
+if (ruleName === undefined || category === undefined) {
+ console.error('node scripts/lint/add.cjs [ruleName] [category]');
+ process.exit(1);
+}
+
+const spacedName = ruleName.replace(/([A-Z+])/g, ' $1').trim().toLowerCase();
+const descriptionKey = spacedName.toUpperCase().replace(/ /g, '_');
+const categoryName = `lint/${ruleName}`;
+
+const ruleLoc = path.join(lintRulesFolder, category, `${ruleName}.ts`);
+const testLoc = path.join(lintRulesFolder, category, `${ruleName}.test.ts`);
+
+write(
+ ruleLoc,
+ `/**
+* Copyright (c) Facebook, Inc. and its affiliates.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+import {Path} from '@romejs/js-compiler';
+import {AnyNode} from '@romejs/js-ast';
+import {descriptions} from '@romejs/diagnostics';
+
+export default {
+ name: '${ruleName}',
+ enter(path: Path): AnyNode {
+ const {node} = path;
+
+ if (false) {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.${descriptionKey},
+ );
+ }
+
+ return node;
+ },
+};
+`,
+);
+
+write(
+ testLoc,
+ `/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import {test} from 'rome';
+import {testLintMultiple} from '../testHelpers';
+
+test(
+ '${spacedName}',
+ async (t) => {
+ await testLintMultiple(
+ t,
+ [
+ // INVALID
+ '',
+ // VALID
+ '',
+ ],
+ {category: '${categoryName}'},
+ );
+ },
+);
+`,
+);
+
+// Add description
+let descriptions = fs.readFileSync(descriptionsFile, 'utf8');
+descriptions = descriptions.replace(
+ 'LINT: {',
+ `LINT: {\n${descriptionKey}: {
+ category: '${categoryName}',
+ message: 'INSERT MESSAGE HERE',
+ },`,
+);
+write(descriptionsFile, descriptions);
+
+require('./update.cjs');

ADD
scripts/lint/update.cjs
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+require('../_setup.cjs');
+
+const {readGeneratedFile, write} = require('../_utils.cjs');
+const {lintRulesFolder, categoriesFile} = require('../_constants.cjs');
+const path = require('path');
+const fs = require('fs');
+
+let defs = [];
+
+for (const category of fs.readdirSync(lintRulesFolder)) {
+ const loc = path.join(lintRulesFolder, category);
+
+ if (fs.statSync(loc).isFile()) {
+ continue;
+ }
+
+ for (const filename of fs.readdirSync(loc)) {
+ if (filename.endsWith('.ts') && !filename.endsWith('.test.ts')) {
+ defs.push({
+ basename: path.basename(filename, path.extname(filename)),
+ category,
+ });
+ }
+ }
+}
+
+defs = defs.sort((a, b) => {
+ return a.basename.localeCompare(b.basename);
+});
+
+// Generate rules index
+
+const indexLoc = path.join(lintRulesFolder, 'index.ts');
+let index = readGeneratedFile(indexLoc);
+
+for (const {basename, category} of defs) {
+ index += `import ${basename} from './${category}/${basename}';\n`;
+}
+
+index += '\n';
+index += 'export const lintTransforms = [\n';
+for (const {basename} of defs) {
+ index += ` ${basename},\n`;
+}
+index += '];\n';
+
+write(indexLoc, index);
+
+// Generate categories
+let categories = readGeneratedFile(categoriesFile);
+categories += 'type LintDiagnosticCategory =';
+for (const {basename} of defs) {
+ categories += `\n | 'lint/${basename}'`;
+}
+categories += ';\n';
+write(categoriesFile, categories);

Add lint rules generator