diff --git a/packages/cli/index.d.ts b/packages/cli/index.d.ts index dfe05be65d94..1b9698add96d 100644 --- a/packages/cli/index.d.ts +++ b/packages/cli/index.d.ts @@ -8,3 +8,5 @@ declare module 'pascalcase' { function pascalcase(input: string): string export default pascalcase } + +declare module 'listr-verbose-renderer' diff --git a/packages/cli/package.json b/packages/cli/package.json index 096bbc263d9c..c4b294218253 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -36,6 +36,7 @@ }, "devDependencies": { "@types/listr": "^0.14.2", + "@types/lodash": "^4.14.151", "@types/node-fetch": "^2.5.5", "@types/pluralize": "^0.0.29", "@types/yargs": "^15.0.5", diff --git a/packages/cli/src/commands/generate/helpers.ts b/packages/cli/src/commands/generate/helpers.ts index 505e19e4f3c5..3628e01d00a9 100644 --- a/packages/cli/src/commands/generate/helpers.ts +++ b/packages/cli/src/commands/generate/helpers.ts @@ -6,9 +6,9 @@ import Listr from 'listr' import pascalcase from 'pascalcase' import { paramCase } from 'param-case' import { CommandModule } from 'yargs' - import { generateTemplate, getPaths, writeFilesTask } from 'src/lib' import c from 'src/lib/colors' +import { Paths } from '@redwoodjs/internal/src/types' /** * Reduces boilerplate for generating an output path and content to write to disk @@ -31,8 +31,8 @@ export const templateForComponentFile = ({ name: string suffix?: string extension?: string - webPathSection?: string - apiPathSection?: string + webPathSection?: keyof Paths['web'] + apiPathSection?: keyof Paths['api'] generator: string templatePath: string templateVars?: {} @@ -93,7 +93,7 @@ export const createYargsForComponentGeneration = ({ }, }, ], - { exitOnError: true } + { collapse: false, exitOnError: true } ) try { diff --git a/packages/cli/src/lib/__tests__/fixtures/code.js b/packages/cli/src/lib/__tests__/fixtures/code.js deleted file mode 100644 index 7631e7768c66..000000000000 --- a/packages/cli/src/lib/__tests__/fixtures/code.js +++ /dev/null @@ -1,2 +0,0 @@ -const line1 = "The quick brown ${pluralCamelName} jumps over the lazy ${foo}."; -const line2 = 'Sphinx of black quartz, judge my vow.' diff --git a/packages/cli/src/lib/__tests__/fixtures/code.ts b/packages/cli/src/lib/__tests__/fixtures/code.ts new file mode 100644 index 000000000000..d81e3f824473 --- /dev/null +++ b/packages/cli/src/lib/__tests__/fixtures/code.ts @@ -0,0 +1,2 @@ +const line1 = 'The quick brown ${pluralCamelName} jumps over the lazy ${foo}.' +const line2 = 'Sphinx of black quartz, judge my vow.' diff --git a/packages/cli/src/lib/__tests__/index.test.js b/packages/cli/src/lib/__tests__/index.test.ts similarity index 99% rename from packages/cli/src/lib/__tests__/index.test.js rename to packages/cli/src/lib/__tests__/index.test.ts index fcd21c31bf61..14c36db94e0c 100644 --- a/packages/cli/src/lib/__tests__/index.test.js +++ b/packages/cli/src/lib/__tests__/index.test.ts @@ -93,7 +93,7 @@ test('generateTemplate returns a lodash-templated string', () => { // Be careful when editing the code.js fixture as the prettifier.config.js will cause it to get // prettified and then it already match the expected output, with no changes test('generateTemplate returns prettified JS code', () => { - const output = index.generateTemplate(path.join('fixtures', 'code.js'), { + const output = index.generateTemplate(path.join('fixtures', 'code.ts'), { root: __dirname, name: 'fox', foo: 'dog', diff --git a/packages/cli/src/lib/colors.js b/packages/cli/src/lib/colors.ts similarity index 100% rename from packages/cli/src/lib/colors.js rename to packages/cli/src/lib/colors.ts diff --git a/packages/cli/src/lib/index.js b/packages/cli/src/lib/index.ts similarity index 82% rename from packages/cli/src/lib/index.js rename to packages/cli/src/lib/index.ts index 474403584add..8c0c70e2d101 100644 --- a/packages/cli/src/lib/index.js +++ b/packages/cli/src/lib/index.ts @@ -1,7 +1,7 @@ import fs from 'fs' import path from 'path' -import lodash from 'lodash/string' +import lodash from 'lodash' import camelcase from 'camelcase' import pascalcase from 'pascalcase' import pluralize from 'pluralize' @@ -16,18 +16,57 @@ import { format } from 'prettier' import c from './colors' -export const asyncForEach = async (array, callback) => { +export const asyncForEach = async (array: any[], callback: Function) => { for (let index = 0; index < array.length; index++) { await callback(array[index], index, array) } } +export const readFile = (target: Parameters[0]) => + fs.readFileSync(target) + +/** + * This wraps the core version of getPaths into something that catches the exception + * and displays a helpful error message. + */ +export const getPaths = () => { + try { + return getRedwoodPaths() + } catch (e) { + console.error(c.error(e.message)) + process.exit(0) + } +} + +/* + * Returns the DMMF defined by `prisma` resolving the relevant `shema.prisma` path. + */ +export const getSchemaDefinitions = async () => { + const schemaPath = path.join(getPaths().api.db, 'schema.prisma') + const metadata = await getDMMF({ + datamodel: readFile(schemaPath).toString(), + }) + + return metadata +} + +/** + * This returns the config present in `prettier.config.js` of a Redwood project. + */ +export const prettierOptions = () => { + try { + return require(path.join(getPaths().base, 'prettier.config.js')) + } catch (e) { + return undefined + } +} + /** * Returns the database schema for the given `name` database table parsed from * the schema.prisma of the target applicaiton. If no `name` is given then the * entire schema is returned. */ -export const getSchema = async (name) => { +export const getSchema = async (name: string) => { const schema = await getSchemaDefinitions() if (name) { @@ -49,10 +88,10 @@ export const getSchema = async (name) => { /** * Returns the enum defined with the given `name` parsed from - * the schema.prisma of the target applicaiton. If no `name` is given then the + * the schema.prisma of the target application. If no `name` is given then the * all enum definitions are returned */ -export const getEnum = async (name) => { +export const getEnum = async (name: string) => { const schema = await getSchemaDefinitions() if (name) { @@ -72,18 +111,6 @@ export const getEnum = async (name) => { return schema.metadata.datamodel.enums } -/* - * Returns the DMMF defined by `prisma` resolving the relevant `shema.prisma` path. - */ -export const getSchemaDefinitions = async () => { - const schemaPath = path.join(getPaths().api.db, 'schema.prisma') - const metadata = await getDMMF({ - datamodel: readFile(schemaPath.toString()), - }) - - return metadata -} - /** * Returns variants of the passed `name` for usage in templates. If the given * name was "fooBar" then these would be: @@ -98,7 +125,7 @@ export const getSchemaDefinitions = async () => { * singularConstantName: FOO_BAR * pluralConstantName: FOO_BARS */ -export const nameVariants = (name) => { +export const nameVariants = (name: string) => { const normalizedName = pascalcase(paramCase(pluralize.singular(name))) return { @@ -117,7 +144,10 @@ export const nameVariants = (name) => { export const templateRoot = path.resolve(__dirname, '../commands/generate') -export const generateTemplate = (templateFilename, { name, root, ...rest }) => { +export const generateTemplate = ( + templateFilename: string, + { name, root, ...rest }: { [key: string]: any } +) => { const templatePath = path.join(root || templateRoot, templateFilename) const template = lodash.template(readFile(templatePath).toString()) @@ -133,7 +163,7 @@ export const generateTemplate = (templateFilename, { name, root, ...rest }) => { const parser = { '.css': 'css', '.js': 'babel', - }[path.extname(templateFilename)] + }[path.extname(templateFilename) as '.css' | '.js'] if (typeof parser === 'undefined') { return renderedTemplate @@ -145,11 +175,9 @@ export const generateTemplate = (templateFilename, { name, root, ...rest }) => { }) } -export const readFile = (target) => fs.readFileSync(target) - export const writeFile = async ( - target, - contents, + target: string, + contents: string | object, { overwriteExisting = false } = {} ) => { if (!overwriteExisting && fs.existsSync(target)) { @@ -162,38 +190,18 @@ export const writeFile = async ( fs.writeFileSync(target, contents) } -export const bytes = (contents) => Buffer.byteLength(contents, 'utf8') - -/** - * This wraps the core version of getPaths into something that catches the exception - * and displays a helpful error message. - */ -export const getPaths = () => { - try { - return getRedwoodPaths() - } catch (e) { - console.error(c.error(e.message)) - process.exit(0) - } -} - -/** - * This returns the config present in `prettier.config.js` of a Redwood project. - */ -export const prettierOptions = () => { - try { - return require(path.join(getPaths().base, 'prettier.config.js')) - } catch (e) { - return undefined - } -} +export const bytes = (contents: Parameters[0]) => + Buffer.byteLength(contents, 'utf8') /** * Creates a list of tasks that write files to the disk. * * @param files - {[filepath]: contents} */ -export const writeFilesTask = (files, options) => { +export const writeFilesTask = ( + files: { [filepath: string]: string }, + options: { overwriteExisting: boolean } +) => { const { base } = getPaths() return new Listr( Object.keys(files).map((file) => { @@ -209,7 +217,7 @@ export const writeFilesTask = (files, options) => { /** * Update the project's routes file. */ -export const addRoutesToRouterTask = (routes) => { +export const addRoutesToRouterTask = (routes: string[]) => { const redwoodPaths = getPaths() const routesContent = readFile(redwoodPaths.web.routes).toString() const newRoutesContent = routes.reverse().reduce((content, route) => { @@ -223,7 +231,15 @@ export const addRoutesToRouterTask = (routes) => { }) } -export const runCommandTask = async (commands, { verbose }) => { +export const runCommandTask = async ( + commands: { + title: string + cmd: string + args: string[] + opts: execa.Options + }[], + { verbose }: { verbose: boolean } +) => { const tasks = new Listr( commands.map(({ title, cmd, args, opts = {} }) => ({ title, @@ -240,6 +256,7 @@ export const runCommandTask = async (commands, { verbose }) => { })), { renderer: verbose && VerboseRenderer, + // @ts-ignore TODO dateFormat comes from listr-verbose-renderer dateFormat: false, } ) diff --git a/packages/cli/src/lib/test.js b/packages/cli/src/lib/test.ts similarity index 93% rename from packages/cli/src/lib/test.js rename to packages/cli/src/lib/test.ts index 0861fb4afa1b..34419f753bb5 100644 --- a/packages/cli/src/lib/test.js +++ b/packages/cli/src/lib/test.ts @@ -43,6 +43,11 @@ export const generatorsRootPath = path.join( 'generate' ) +// Returns the contents of a text file suffixed with ".fixture" +export const loadFixture = (filepath: string) => { + return fs.readFileSync(filepath).toString() +} + // Loads the fixture for a generator by assuming a lot of the path structure automatically: // // loadGeneratorFixture('scaffold', 'NamePage.js') @@ -50,7 +55,7 @@ export const generatorsRootPath = path.join( // will return the contents of: // // cli/src/commands/generate/scaffold/test/fixtures/NamePage.js.fixture -export const loadGeneratorFixture = (generator, name) => { +export const loadGeneratorFixture = (generator: string, name: string) => { return loadFixture( path.join( __dirname, @@ -64,8 +69,3 @@ export const loadGeneratorFixture = (generator, name) => { ) ) } - -// Returns the contents of a text file suffixed with ".fixture" -export const loadFixture = (filepath) => { - return fs.readFileSync(filepath).toString() -} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index cdc6e961982a..18ddc1f00878 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "baseUrl": ".", "rootDir": "src", - "outDir": "dist" + "outDir": "dist", + "esModuleInterop": true }, "include": ["src", "index.d.ts"], } diff --git a/yarn.lock b/yarn.lock index 1b6731fc0a46..fbfc12ff711a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2722,6 +2722,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.150.tgz#649fe44684c3f1fcb6164d943c5a61977e8cf0bd" integrity sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w== +"@types/lodash@^4.14.151": + version "4.14.151" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.151.tgz#7d58cac32bedb0ec37cb7f99094a167d6176c9d5" + integrity sha512-Zst90IcBX5wnwSu7CAS0vvJkTjTELY4ssKbHiTnGcJgi170uiS8yQDdc3v6S77bRqYQIN1App5a1Pc2lceE5/g== + "@types/long@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"