Sweep: Convert the engine.js file to typescript
kristiandupont opened this issue Β· comments
The file src/engine.js
is currently plain Javascript. It should be typescript instead.
Here's the PR! #290.
β‘ Sweep Free Trial: I used GPT-4 to create this ticket. You have 5 GPT-4 tickets left. For more GPT-4 tickets, visit our payment portal.To get Sweep to recreate this ticket, leave a comment prefixed with "sweep:" or edit the issue.
- Install Sweep Configs: Pull Request
Step 1: π Code Search
I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.
Some code snippets I looked at (click to expand). If some file is missing from here, you can mention the path in the ticket description.
Lines 1 to 119 in a5639d6
/* eslint-disable unicorn/prefer-module */ | |
import chalk from 'chalk'; | |
import { extractSchemas } from 'extract-pg-schema'; | |
import path from 'path'; | |
import { indexBy, keys, prop, values } from 'ramda'; | |
import * as builtinRules from './rules'; | |
function consoleReporter({ rule, identifier, message }) { | |
console.error( | |
`${chalk.yellow(identifier)}: error ${chalk.red(rule)} : ${message}`, | |
); | |
} | |
let anyIssues = false; | |
const suggestedMigrations = []; | |
const createReportFunction = | |
(reporter, ignoreMatchers) => | |
({ rule, identifier, message, suggestedMigration }) => { | |
if (ignoreMatchers.some((im) => im(rule, identifier))) { | |
// This one is ignored. | |
return; | |
} | |
reporter({ rule, identifier, message }); | |
if (suggestedMigration) { | |
suggestedMigrations.push(suggestedMigration); | |
} | |
anyIssues = true; | |
}; | |
export async function processDatabase({ | |
connection, | |
plugins = [], | |
rules, | |
schemas, | |
ignores = [], | |
}) { | |
const pluginRules = plugins.map((p) => require(path.join(process.cwd(), p))); | |
const allRules = [builtinRules, ...pluginRules].reduce( | |
(acc, elem) => ({ ...acc, ...elem }), | |
{}, | |
); | |
const registeredRules = indexBy(prop('name'), values(allRules)); | |
console.info( | |
`Connecting to ${chalk.greenBright(connection.database)} on ${ | |
connection.host | |
}`, | |
); | |
const ignoreMatchers = ignores.map((i) => (rule, identifier) => { | |
let ruleMatch; | |
if (i.rule) { | |
ruleMatch = rule === i.rule; | |
} else if (i.rulePattern) { | |
ruleMatch = new RegExp(i.rulePattern).test(rule); | |
} else { | |
throw new Error( | |
`Ignore object is missing a rule or rulePattern property: ${JSON.stringify( | |
i, | |
)}`, | |
); | |
} | |
let identifierMatch; | |
if (i.identifier) { | |
identifierMatch = identifier === i.identifier; | |
} else if (i.identifierPattern) { | |
identifierMatch = new RegExp(i.identifierPattern).test(identifier); | |
} else { | |
throw new Error( | |
`Ignore object is missing an identifier or identifierPattern property: ${JSON.stringify( | |
i, | |
)}`, | |
); | |
} | |
return ruleMatch && identifierMatch; | |
}); | |
const report = createReportFunction(consoleReporter, ignoreMatchers); | |
const extractedSchemas = await extractSchemas(connection, { | |
schemas: schemas.map((s) => s.name), | |
}); | |
for (const schema of schemas) { | |
const schemaObject = extractedSchemas[schema.name]; | |
const mergedRules = { | |
...rules, | |
...schema.rules, | |
}; | |
for (const ruleKey of keys(mergedRules)) { | |
if (!(ruleKey in registeredRules)) { | |
throw new Error(`Unknown rule: "${ruleKey}"`); | |
} | |
const [state, ...options] = mergedRules[ruleKey]; | |
if (state === 'error') { | |
registeredRules[ruleKey].process({ schemaObject, report, options }); | |
} | |
} | |
} | |
if (anyIssues) { | |
if (suggestedMigrations.length > 0) { | |
console.info(''); | |
console.info('Suggested fix'); | |
for (const sf of suggestedMigrations) console.info(sf); | |
} | |
return 1; | |
} | |
console.info('No issues detected'); | |
return 0; | |
} |
Lines 27 to 65 in a5639d6
Connecting to dvdrental on localhost | |
public.address.address: error prefer-text-to-varchar : Prefer text to varchar types | |
public.address.address2: error prefer-text-to-varchar : Prefer text to varchar types | |
public.address.district: error prefer-text-to-varchar : Prefer text to varchar types | |
public.address.postal_code: error prefer-text-to-varchar : Prefer text to varchar types | |
public.address.phone: error prefer-text-to-varchar : Prefer text to varchar types | |
public.category.name: error prefer-text-to-varchar : Prefer text to varchar types | |
public.country.country: error prefer-text-to-varchar : Prefer text to varchar types | |
public.customer.first_name: error prefer-text-to-varchar : Prefer text to varchar types | |
public.customer.last_name: error prefer-text-to-varchar : Prefer text to varchar types | |
public.customer.email: error prefer-text-to-varchar : Prefer text to varchar types | |
public.film.title: error prefer-text-to-varchar : Prefer text to varchar types | |
public.staff.first_name: error prefer-text-to-varchar : Prefer text to varchar types | |
public.staff.last_name: error prefer-text-to-varchar : Prefer text to varchar types | |
public.staff.email: error prefer-text-to-varchar : Prefer text to varchar types | |
public.staff.username: error prefer-text-to-varchar : Prefer text to varchar types | |
public.staff.password: error prefer-text-to-varchar : Prefer text to varchar types | |
Suggested fix | |
ALTER TABLE "address" ALTER COLUMN "address" TYPE TEXT; | |
ALTER TABLE "address" ALTER COLUMN "address2" TYPE TEXT; | |
ALTER TABLE "address" ALTER COLUMN "district" TYPE TEXT; | |
ALTER TABLE "address" ALTER COLUMN "postal_code" TYPE TEXT; | |
ALTER TABLE "address" ALTER COLUMN "phone" TYPE TEXT; | |
ALTER TABLE "category" ALTER COLUMN "name" TYPE TEXT; | |
ALTER TABLE "country" ALTER COLUMN "country" TYPE TEXT; | |
ALTER TABLE "customer" ALTER COLUMN "first_name" TYPE TEXT; | |
ALTER TABLE "customer" ALTER COLUMN "last_name" TYPE TEXT; | |
ALTER TABLE "customer" ALTER COLUMN "email" TYPE TEXT; | |
ALTER TABLE "film" ALTER COLUMN "title" TYPE TEXT; | |
ALTER TABLE "staff" ALTER COLUMN "first_name" TYPE TEXT; | |
ALTER TABLE "staff" ALTER COLUMN "last_name" TYPE TEXT; | |
ALTER TABLE "staff" ALTER COLUMN "email" TYPE TEXT; | |
ALTER TABLE "staff" ALTER COLUMN "username" TYPE TEXT; | |
ALTER TABLE "staff" ALTER COLUMN "password" TYPE TEXT; | |
error Command failed with exit code 1. | |
``` | |
You can play around with the configuration options in `.schemalintrc.js` file to experiment. |
Lines 1 to 69 in a5639d6
{ | |
"compilerOptions": { | |
/* Basic Options */ | |
// "incremental": true, /* Enable incremental compilation */ | |
"target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, | |
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, | |
// "lib": [], /* Specify library files to be included in the compilation. */ | |
"allowJs": true /* Allow javascript files to be compiled. */, | |
// "checkJs": true /* Report errors in .js files. */, | |
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ | |
"declaration": true /* Generates corresponding '.d.ts' file. */, | |
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ | |
// "sourceMap": true, /* Generates corresponding '.map' file. */ | |
// "outFile": "./", /* Concatenate and emit output to single file. */ | |
"outDir": "./build" /* Redirect output structure to the directory. */, | |
"rootDir": "./src/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | |
"resolveJsonModule": true, | |
// "composite": true, /* Enable project compilation */ | |
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ | |
// "removeComments": true, /* Do not emit comments to output. */ | |
// "noEmit": true, /* Do not emit outputs. */ | |
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ | |
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ | |
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ | |
/* Strict Type-Checking Options */ | |
"strict": true /* Enable all strict type-checking options. */, | |
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ | |
// "strictNullChecks": true, /* Enable strict null checks. */ | |
// "strictFunctionTypes": true, /* Enable strict checking of function types. */ | |
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ | |
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ | |
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ | |
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ | |
/* Additional Checks */ | |
// "noUnusedLocals": true, /* Report errors on unused locals. */ | |
// "noUnusedParameters": true, /* Report errors on unused parameters. */ | |
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ | |
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ | |
/* Module Resolution Options */ | |
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ | |
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ | |
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ | |
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ | |
// "typeRoots": [], /* List of folders to include type definitions from. */ | |
// "types": [], /* Type declaration files to be included in compilation. */ | |
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | |
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, | |
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ | |
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | |
/* Source Map Options */ | |
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | |
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | |
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ | |
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ | |
/* Experimental Options */ | |
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ | |
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ | |
/* Advanced Options */ | |
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, | |
"skipLibCheck": true | |
}, | |
"include": ["src"] | |
} |
Lines 1 to 74 in a5639d6
/* eslint-disable unicorn/prefer-module */ | |
/* eslint-disable unicorn/no-process-exit */ | |
import chalk from 'chalk'; | |
// @ts-ignore | |
import optionator from 'optionator'; | |
import path from 'path'; | |
import { processDatabase } from './engine'; | |
// @ts-ignore | |
const { version } = require('../package.json'); | |
async function main() { | |
const o = optionator({ | |
prepend: 'Usage: schemalint [options]', | |
append: `Version ${version}`, | |
options: [ | |
{ | |
option: 'help', | |
alias: 'h', | |
type: 'Boolean', | |
description: 'displays help', | |
}, | |
{ | |
option: 'version', | |
alias: 'v', | |
type: 'Boolean', | |
description: 'displays version', | |
}, | |
{ | |
option: 'config', | |
alias: 'c', | |
type: 'path::String', | |
description: | |
'Use this configuration, overriding .schemalintrc.* config options if present', | |
}, | |
], | |
}); | |
let options; | |
try { | |
options = o.parseArgv(process.argv); | |
} catch (error) { | |
console.error(error.message); | |
process.exit(1); | |
} | |
if (options.help) { | |
console.info(o.generateHelp()); | |
process.exit(0); | |
} | |
if (options.version) { | |
console.info(version); | |
process.exit(0); | |
} | |
console.info(`${chalk.greenBright('schema-lint')}`); | |
const configFile = path.join( | |
process.cwd(), | |
options.config || '.schemalintrc.js', | |
); | |
try { | |
const config = require(configFile); | |
const exitCode = await processDatabase(config); | |
process.exit(exitCode); | |
} catch (error) { | |
console.error(error); | |
process.exit(1); | |
} | |
} | |
main(); |
schemalint/src/rules/nameCasing.test.js
Lines 1 to 39 in a5639d6
import { describe, expect, it, test, vi } from 'vitest'; | |
import { nameCasing } from './nameCasing'; | |
describe('nameCasing', () => { | |
it('no tables or views passed no errors', () => { | |
const mockReporter = vi.fn(); | |
nameCasing.process({ | |
options: [], | |
schemaObject: { | |
tables: [], | |
views: [], | |
}, | |
report: mockReporter, | |
}); | |
expect(mockReporter).toBeCalledTimes(0); | |
}); | |
test.each` | |
type | param | expected1 | expected2 | |
${`default`} | ${null} | ${`th_is_is_no_sn_a_k_e_ca_s_e`} | ${`neit_he_r_is_this`} | |
${`snake-case`} | ${`snake`} | ${`th_is_is_no_sn_a_k_e_ca_s_e`} | ${`neit_he_r_is_this`} | |
${`dash case`} | ${`dash`} | ${`th-is-is-no-sn-a-k-e-ca-s-e`} | ${`neit-he-r-is-this`} | |
${`camel case`} | ${`camel`} | ${`thIsIsNoSnAKECaSE`} | ${`neitHeRIsThis`} | |
${`pascal case`} | ${`pascal`} | ${`ThIsIsNoSnAKECaSE`} | ${`NeitHeRIsThis`} | |
`( | |
'$type : param of $param applies to table names and requires $expected', | |
({ _type, param, expected1, expected2 }) => { | |
const mockReporter = vi.fn(); | |
nameCasing.process({ | |
options: [param], | |
schemaObject: { | |
name: 'schema', | |
tables: [ | |
{ name: 'Th-IsIsNoSnA_kECaSE', columns: [] }, | |
{ name: 'neit_he-rIsThis', columns: [] }, | |
], |
Summaries of links found in the content:
Step 2: π§ Snippet Analysis
From looking through the relevant snippets, I decided to make the following modifications:
File Path | Proposed Changes |
---|---|
src/engine.js |
Rename this file to engine.ts . Update the syntax to TypeScript, including adding type annotations to variables, function parameters, and return types. Ensure that any imported modules have TypeScript definitions available. If not, install the appropriate @types package or declare custom type definitions. |
src/cli.js |
Update the import statement of processDatabase to import from src/engine.ts instead of src/engine.js . |
Step 3: π Planning
I have created a plan for writing the pull request. I am now working my plan and coding the required changes to address this issue. Here is the planned pull request:
Transition src/engine.js from Javascript to Typescript
sweep/transition-engine-js-to-ts
Description
This PR addresses the Github issue #288 by transitioning the
src/engine.js
file from JavaScript to TypeScript. The goal of this transition is to improve the codebase's standardization, catch potential bugs with static typing, and enhance code readability and maintainability.Summary of Changes
- Renamed
src/engine.js
tosrc/engine.ts
- Updated the syntax of
src/engine.ts
to TypeScript, including adding type annotations to variables, function parameters, and return types- Ensured that imported modules in
src/engine.ts
have TypeScript definitions available- Updated the import statement of
processDatabase
insrc/cli.js
to import fromsrc/engine.ts
instead ofsrc/engine.js
- No changes were required in the
tsconfig.json
file as theinclude
option already covers thesrc
directoryPlease review the changes and let me know if any further modifications are needed.
Step 4: β¨οΈ Coding
I have finished coding the issue. I am now reviewing it for completeness.
Step 5: π Code Review
Success! π
To recreate the pull request, leave a comment prefixed with "sweep:" or edit the issue.
Join Our Discord