diff --git a/packages/eslint-config/src/overrides/buildBaseTypescript.ts b/packages/eslint-config/src/overrides/buildBaseTypescript.ts index 4952fac6..11426538 100644 --- a/packages/eslint-config/src/overrides/buildBaseTypescript.ts +++ b/packages/eslint-config/src/overrides/buildBaseTypescript.ts @@ -70,6 +70,7 @@ export function buildBaseTypescript( ...rules, ...eslintTypescriptRules, '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-unsafe-enum-comparison': 'off', '@typescript-eslint/prefer-nullish-coalescing': [ 'error', { diff --git a/packages/eslint-config/src/rules/namingConvention.ts b/packages/eslint-config/src/rules/namingConvention.ts index 0f48169c..1b976fcc 100644 --- a/packages/eslint-config/src/rules/namingConvention.ts +++ b/packages/eslint-config/src/rules/namingConvention.ts @@ -66,7 +66,7 @@ export const namingRules: Linter.RulesRecord = { format: ['PascalCase', 'camelCase'], }, { - selector: ['classMethod', 'objectLiteralMethod'], + selector: ['function', 'classMethod', 'objectLiteralMethod'], format: ['PascalCase', 'camelCase'], }, { diff --git a/packages/eslint-config/tsconfig.build.json b/packages/eslint-config/tsconfig.build.json index c74d811e..34126b27 100644 --- a/packages/eslint-config/tsconfig.build.json +++ b/packages/eslint-config/tsconfig.build.json @@ -12,7 +12,7 @@ "rootDir": "src", "outDir": "dist", "paths": {}, - "target": "es5", + "target": "es6", "module": "commonjs" }, "exclude": ["node_modules", "*.spec.ts"], diff --git a/packages/eslint-plugin/__tests__/forbiddenImports.test.ts b/packages/eslint-plugin/__tests__/forbiddenImports.test.ts index 540c8930..07476245 100644 --- a/packages/eslint-plugin/__tests__/forbiddenImports.test.ts +++ b/packages/eslint-plugin/__tests__/forbiddenImports.test.ts @@ -14,8 +14,12 @@ const ruleTester = new RuleTester({ const filename = './test_src/default.tsx'; -ruleTester.run('forbiddenImports > lodash', rule, { - valid: [{ code: `import merge from 'lodash/merge';`, filename }], +ruleTester.run('forbiddenImports > valid other import formats', rule, { + valid: [ + { code: `import 'moment/locales/en';`, filename }, + { code: `import something from 'moment';`, filename }, + { code: `import { something } from 'moment';`, filename }, + ], invalid: [ { code: `import { merge } from 'lodash';`, @@ -30,6 +34,12 @@ ruleTester.run('forbiddenImports > lodash', rule, { }, ], }, + ], +}); + +ruleTester.run('forbiddenImports > lodash', rule, { + valid: [{ code: `import merge from 'lodash/merge';`, filename }], + invalid: [ { code: `import { merge as _merge } from 'lodash';`, output: `import _merge from 'lodash/merge';`, diff --git a/packages/eslint-plugin/src/forbiddenImports.ts b/packages/eslint-plugin/src/forbiddenImports.ts index ac0dba7f..5e947441 100644 --- a/packages/eslint-plugin/src/forbiddenImports.ts +++ b/packages/eslint-plugin/src/forbiddenImports.ts @@ -74,6 +74,7 @@ export const forbiddenImports: TSESLint.RuleModule = { defaultOptions: [], create: (context) => ({ ImportDeclaration(node) { + if (node.specifiers.length === 0) return; const importName = node.source.value || ''; const scope = context.getScope(); diff --git a/packages/eslint-plugin/tsconfig.build.json b/packages/eslint-plugin/tsconfig.build.json index c74d811e..34126b27 100644 --- a/packages/eslint-plugin/tsconfig.build.json +++ b/packages/eslint-plugin/tsconfig.build.json @@ -12,7 +12,7 @@ "rootDir": "src", "outDir": "dist", "paths": {}, - "target": "es5", + "target": "es6", "module": "commonjs" }, "exclude": ["node_modules", "*.spec.ts"], diff --git a/packages/nx/src/executors/quality/executor.ts b/packages/nx/src/executors/quality/executor.ts index 3939b3a2..554e7f5d 100644 --- a/packages/nx/src/executors/quality/executor.ts +++ b/packages/nx/src/executors/quality/executor.ts @@ -23,8 +23,11 @@ export default async function runExecutor( const root = metadata.root || context.root; try { if (options.checkConfig) configCheck(root); - await packageCheck({ directory: root, shouldFix: options.fix }); - return await lintRun( + const packageCheckResult = await packageCheck({ + directory: root, + shouldFix: options.fix, + }); + const lintResult = await lintRun( { ...options, noEslintrc: false, @@ -36,6 +39,7 @@ export default async function runExecutor( }, context, ); + return { success: lintResult.success && packageCheckResult.success }; } catch (e) { console.log(e as Error); return { diff --git a/packages/nx/src/executors/quality/package.ts b/packages/nx/src/executors/quality/package.ts index 07ef80e4..78c8e335 100644 --- a/packages/nx/src/executors/quality/package.ts +++ b/packages/nx/src/executors/quality/package.ts @@ -62,11 +62,6 @@ export async function packageCheck({ directory: string; shouldFix: boolean; }) { - console.log( - chalk.cyan( - ` ${icons.info} We recommend using \`npm-upgrade\` to manage dependencies.\n`, - ), - ); const result = await processPackage({ packageDir: directory, shouldWriteFile: shouldFix, @@ -93,6 +88,14 @@ export async function packageCheck({ return Promise.resolve(appPackage); }, }); + if (result.success && result.error === 'no-file') { + return result; + } + console.log( + chalk.cyan( + ` ${icons.info} We recommend using \`npm-upgrade\` to manage dependencies.\n`, + ), + ); if (result.success) { console.log(chalk.green(`${icons.check} Package dependencies validated`)); } diff --git a/packages/nx/src/generators/quality/generator.ts b/packages/nx/src/generators/quality/generator.ts index 5ef353b6..00620c3c 100644 --- a/packages/nx/src/generators/quality/generator.ts +++ b/packages/nx/src/generators/quality/generator.ts @@ -26,10 +26,10 @@ function updateProjectConfig(tree: Tree, projectName: string) { outputs: ['{options.outputFile}'], options: { lintFilePatterns: [ - '{projectRoot}/src/**/*.ts', - '{projectRoot}/src/**/*.tsx', - '{projectRoot}/src/**/*.js', - '{projectRoot}/src/**/*.jsx', + 'src/**/*.ts', + 'src/**/*.tsx', + 'src/**/*.js', + 'src/**/*.jsx', ], }, }; @@ -103,8 +103,8 @@ export async function qualityGenerator( cwd: process.cwd(), stdio: 'inherit', }); - await generateConfig(tree); - await generateIcons(tree); + await generateConfig(tree, schema); + await generateIcons(tree, schema); await generateFileTypes(tree, schema); await formatFiles(tree); } diff --git a/packages/nx/src/generators/ts-carbon-icons/generator.ts b/packages/nx/src/generators/ts-carbon-icons/generator.ts index ffcb4053..e912b3fe 100644 --- a/packages/nx/src/generators/ts-carbon-icons/generator.ts +++ b/packages/nx/src/generators/ts-carbon-icons/generator.ts @@ -1,19 +1,36 @@ import * as path from 'path'; -import { Tree } from '@nx/devkit'; +import { Tree, getProjects } from '@nx/devkit'; import { detectInstalledVersion, outputPrettyFile, } from '@tablecheck/frontend-utils'; -export async function tsCarbonIconsGenerator(tree: Tree) { - const projectRoot = tree.root; +export async function tsCarbonIconsGenerator( + tree: Tree, + schema: { project: string }, +) { + const project = getProjects(tree).get(schema.project); + if (!project) { + console.warn(`Project ${schema.project} not found`); + return; + } + const projectRoot = path.join(tree.root, project.root); try { - const carbonPackageJsonPath = detectInstalledVersion( - projectRoot, - '@carbon/icons-react', - '11', - ); + let carbonPackageJsonPath: string; + try { + carbonPackageJsonPath = detectInstalledVersion( + projectRoot, + '@carbon/icons-react', + '11', + ); + } catch (e) { + carbonPackageJsonPath = detectInstalledVersion( + tree.root, + '@carbon/icons-react', + '11', + ); + } // eslint-disable-next-line @typescript-eslint/no-var-requires -- await import throws segfault errors as this is common Js const carbonIcons = require(path.join( carbonPackageJsonPath, @@ -22,7 +39,7 @@ export async function tsCarbonIconsGenerator(tree: Tree) { const fileContent = `${Object.keys(carbonIcons).reduce( (result, iconName) => `${result} declare export const ${iconName}: CarbonIcon;\n`, - `/* this file is generated during configuring typescript */ + `// this file is generated with \`nx generate @tablecheck/nx:ts-carbon-icons ${schema.project}\` declare module '@carbon/icons-react' { declare export type CarbonIconSize = 16 | 20 | 24 | 32; declare export type CarbonIcon = React.ForwardRefExoticComponent< @@ -32,8 +49,11 @@ export async function tsCarbonIconsGenerator(tree: Tree) { >; `, )}\n}`; + const definitionsPath = project.sourceRoot + ? path.join(project.sourceRoot, 'definitions') + : 'definitions'; await outputPrettyFile( - path.join(projectRoot, 'src', 'definitions', 'carbonIcons.gen.d.ts'), + path.join(projectRoot, definitionsPath, 'carbonIcons.gen.d.ts'), fileContent, ); } catch (e) { diff --git a/packages/nx/src/generators/ts-carbon-icons/schema.json b/packages/nx/src/generators/ts-carbon-icons/schema.json index dbba393d..cebe6291 100644 --- a/packages/nx/src/generators/ts-carbon-icons/schema.json +++ b/packages/nx/src/generators/ts-carbon-icons/schema.json @@ -3,5 +3,17 @@ "$id": "TsCarbonIcons", "title": "Generate a typescript definition file for @carbon/icons", "type": "object", - "properties": {} + "properties": { + "project": { + "type": "string", + "description": "The name of the project.", + "alias": "p", + "$default": { + "$source": "projectName" + }, + "x-prompt": "What is the name of the project for the generator?", + "x-priority": "important" + } + }, + "required": ["project"] } diff --git a/packages/nx/src/generators/ts-node-config/generator.ts b/packages/nx/src/generators/ts-node-config/generator.ts index a7819fec..65cbac38 100644 --- a/packages/nx/src/generators/ts-node-config/generator.ts +++ b/packages/nx/src/generators/ts-node-config/generator.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -import { Tree } from '@nx/devkit'; +import { Tree, getProjects } from '@nx/devkit'; import { detectInstalledVersion, outputPrettyFile, @@ -34,14 +34,44 @@ function buildTypes(configValue: unknown): string { } } -export async function tsNodeConfigGenerator(tree: Tree) { - const projectRoot = tree.root; +function getConfigBasePath(root: string, projectRoot: string) { + const projectConfigPath = path.join(projectRoot, 'config'); + if (fs.existsSync(projectConfigPath)) { + return projectConfigPath; + } + const rootConfigPath = path.join(root, 'config'); + if (fs.existsSync(rootConfigPath)) { + return rootConfigPath; + } + throw new Error( + `No config directory found at ${projectConfigPath} or ${rootConfigPath} for project ${projectRoot}`, + ); +} + +export async function tsNodeConfigGenerator( + tree: Tree, + schema: { project: string }, +) { + const project = getProjects(tree).get(schema.project); + if (!project) { + console.warn(`Project ${schema.project} not found`); + return; + } + const projectRoot = path.join(tree.root, project.root); try { - detectInstalledVersion(projectRoot, 'config', '*'); + try { + detectInstalledVersion(projectRoot, 'config', '*'); + } catch (e) { + detectInstalledVersion(tree.root, 'config', '*'); + } - const defaultConfigFilePath = path.join(projectRoot, 'config/default.json'); - const devConfigFilePath = path.join(projectRoot, 'config/development.json'); - if (!fs.existsSync(defaultConfigFilePath)) return; + const configBasePath = getConfigBasePath(tree.root, projectRoot); + const defaultConfigFilePath = path.join(configBasePath, 'default.json'); + const devConfigFilePath = path.join(configBasePath, 'development.json'); + if (!fs.existsSync(defaultConfigFilePath)) { + console.info('No default config found, skipping config generation'); + return; + } const defaultConfigJson = fs.readJsonSync(defaultConfigFilePath) as Record< string, @@ -50,8 +80,10 @@ export async function tsNodeConfigGenerator(tree: Tree) { const devConfigJson = ( fs.existsSync(devConfigFilePath) ? fs.readJSONSync(devConfigFilePath) : {} ) as Record; - const fileContent = `declare module '@tablecheck/scripts' { - // this file is autobuilt with \`nx generate @tablecheck/nx:ts-node-config\`, all changes here will be overwritten + const fileContent = `declare module 'config' { + // this file is generated with \`nx generate @tablecheck/nx:ts-node-config ${ + schema.project + }\` interface DefaultConfig ${buildTypes(defaultConfigJson)} export interface DevelopmentConfig extends DefaultConfig ${buildTypes( devConfigJson, @@ -60,9 +92,14 @@ export async function tsNodeConfigGenerator(tree: Tree) { global { const CONFIG: DevelopmentConfig; } + const config: DevelopmentConfig; + export default config; }`; + const definitionsPath = project.sourceRoot + ? path.join(project.sourceRoot, 'definitions') + : 'definitions'; await outputPrettyFile( - path.join(projectRoot, 'src', 'definitions', 'nodeConfig.gen.d.ts'), + path.join(projectRoot, definitionsPath, 'nodeConfig.gen.d.ts'), fileContent, ); } catch (e) { diff --git a/packages/nx/src/generators/ts-node-config/schema.json b/packages/nx/src/generators/ts-node-config/schema.json index 9d070aa0..0429bc7c 100644 --- a/packages/nx/src/generators/ts-node-config/schema.json +++ b/packages/nx/src/generators/ts-node-config/schema.json @@ -3,5 +3,17 @@ "$id": "TsNodeConfig", "title": "Generate a typescript definition file for node-config default.json", "type": "object", - "properties": {} + "properties": { + "project": { + "type": "string", + "description": "The name of the project.", + "alias": "p", + "$default": { + "$source": "projectName" + }, + "x-prompt": "What is the name of the project for the generator?", + "x-priority": "important" + } + }, + "required": ["project"] } diff --git a/packages/utils/src/packageJson.ts b/packages/utils/src/packageJson.ts index abde055a..041cd221 100644 --- a/packages/utils/src/packageJson.ts +++ b/packages/utils/src/packageJson.ts @@ -18,6 +18,10 @@ export function getPackageJson(directory = process.cwd()) { return fs.readJsonSync(path.join(directory, 'package.json')) as PackageJson; } +export function hasPackageJson(directory = process.cwd()) { + return fs.existsSync(path.join(directory, 'package.json')); +} + export function detectInstalledVersion( cwd: string, packageName: string, @@ -153,6 +157,9 @@ export async function processPackage({ shouldWriteFile: boolean; }) { try { + if (!hasPackageJson(packageDir)) { + return { success: true, error: 'no-file' }; + } const packageContent = getPackageJson(packageDir); let didSucceed = true; let processingError: Error | undefined;