Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(esm): get lint working #10152

Merged
merged 26 commits into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changesets/10152.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
- fix(esm): get lint working (#10152) by @jtoar

This PR fixes `yarn rw lint` and some generators and setup commands for ESM projects.
Before projects would get the following error:

```
Oops! Something went wrong! :(

ESLint: 8.55.0

Error [ERR_REQUIRE_ESM]: require() of ES Module ~/redwood-app/prettier.config.js from ~/redwood-app/node_modules/prettier/third-party.js not supported.
Instead change the require of prettier.config.js in ~/redwood-app/node_modules/prettier/third-party.js to a dynamic import() which is available in all CommonJS modules.
```
12 changes: 6 additions & 6 deletions packages/cli-helpers/src/auth/__tests__/authFiles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ beforeEach(() => {
vi.mocked(isTypeScriptProject).mockReturnValue(true)
})

it('generates a record of TS files', () => {
it('generates a record of TS files', async () => {
const filePaths = Object.keys(
apiSideFiles({
await apiSideFiles({
basedir: path.join(__dirname, 'fixtures/supertokensSetup'),
webAuthn: false,
})
Expand All @@ -54,11 +54,11 @@ it('generates a record of TS files', () => {
])
})

it('generates a record of JS files', () => {
it('generates a record of JS files', async () => {
vi.mocked(isTypeScriptProject).mockReturnValue(false)

const filePaths = Object.keys(
apiSideFiles({
await apiSideFiles({
basedir: path.join(__dirname, 'fixtures/supertokensSetup'),
webAuthn: false,
})
Expand All @@ -71,8 +71,8 @@ it('generates a record of JS files', () => {
])
})

it('generates a record of webAuthn files', () => {
const filesRecord = apiSideFiles({
it('generates a record of webAuthn files', async () => {
const filesRecord = await apiSideFiles({
basedir: path.join(__dirname, 'fixtures/dbAuthSetup'),
webAuthn: true,
})
Expand Down
4 changes: 2 additions & 2 deletions packages/cli-helpers/src/auth/__tests__/authTasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ describe('authTasks', () => {
).toMatchSnapshot()
})

it('writes an auth.js file for JS projects', () => {
it('writes an auth.js file for JS projects', async () => {
vi.mocked(isTypeScriptProject).mockReturnValue(false)

vol.fromJSON({
Expand All @@ -656,7 +656,7 @@ describe('authTasks', () => {
provider: 'auth0',
setupMode: 'FORCE',
}
createWebAuth(getPaths().base, false).task(ctx)
await createWebAuth(getPaths().base, false).task(ctx)

expect(
fs.readFileSync(path.join(getPaths().web.src, 'auth.js'), 'utf-8')
Expand Down
121 changes: 58 additions & 63 deletions packages/cli-helpers/src/auth/authFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,76 +24,71 @@ interface FilesArgs {
* }
* ```
*/
export const apiSideFiles = ({ basedir, webAuthn }: FilesArgs) => {
export const apiSideFiles = async ({ basedir, webAuthn }: FilesArgs) => {
const apiSrcPath = getPaths().api.src
const apiBaseTemplatePath = path.join(basedir, 'templates', 'api')
const templateDirectories = fs.readdirSync(apiBaseTemplatePath)

const filesRecord = templateDirectories.reduce<Record<string, string>>(
(acc, dir) => {
const templateFiles = fs.readdirSync(path.join(apiBaseTemplatePath, dir))
const filePaths = templateFiles
.filter((fileName) => {
const fileNameParts = fileName.split('.')
// Remove all webAuthn files. We'll handle those in the next step
return (
fileNameParts.length <= 3 || fileNameParts.at(-3) !== 'webAuthn'
)
})
.map((fileName) => {
// remove "template" from the end, and change from {ts,tsx} to {js,jsx} for
// JavaScript projects
let outputFileName = fileName.replace(/\.template$/, '')
if (!isTypeScriptProject()) {
outputFileName = outputFileName.replace(/\.ts(x?)$/, '.js$1')
}

if (!webAuthn) {
return { templateFileName: fileName, outputFileName }
}

// Insert "webAuthn." before the second to last part
const webAuthnFileName = fileName
.split('.')
.reverse()
.map((part, i) => (i === 1 ? 'webAuthn.' + part : part))
.reverse()
.join('.')

// Favor the abc.xyz.webAuthn.ts.template file if it exists, otherwise
// just go with the "normal" filename
if (templateFiles.includes(webAuthnFileName)) {
return { templateFileName: webAuthnFileName, outputFileName }
} else {
return { templateFileName: fileName, outputFileName }
}
})
.map((f) => {
const templateFilePath = path.join(
apiBaseTemplatePath,
dir,
f.templateFileName
)
const outputFilePath = path.join(apiSrcPath, dir, f.outputFileName)

return { templateFilePath, outputFilePath }
})

filePaths.forEach((paths) => {
const content = fs.readFileSync(paths.templateFilePath, 'utf8')

acc = {
...acc,
[paths.outputFilePath]: isTypeScriptProject()
? content
: transformTSToJS(paths.outputFilePath, content),
let filesRecord: Record<string, string> = {}

for (const dir of templateDirectories) {
const templateFiles = fs.readdirSync(path.join(apiBaseTemplatePath, dir))
const filePaths = templateFiles
.filter((fileName) => {
const fileNameParts = fileName.split('.')
// Remove all webAuthn files. We'll handle those in the next step
return fileNameParts.length <= 3 || fileNameParts.at(-3) !== 'webAuthn'
})
.map((fileName) => {
// remove "template" from the end, and change from {ts,tsx} to {js,jsx} for
// JavaScript projects
let outputFileName = fileName.replace(/\.template$/, '')
if (!isTypeScriptProject()) {
outputFileName = outputFileName.replace(/\.ts(x?)$/, '.js$1')
}

if (!webAuthn) {
return { templateFileName: fileName, outputFileName }
}

// Insert "webAuthn." before the second to last part
const webAuthnFileName = fileName
.split('.')
.reverse()
.map((part, i) => (i === 1 ? 'webAuthn.' + part : part))
.reverse()
.join('.')

// Favor the abc.xyz.webAuthn.ts.template file if it exists, otherwise
// just go with the "normal" filename
if (templateFiles.includes(webAuthnFileName)) {
return { templateFileName: webAuthnFileName, outputFileName }
} else {
return { templateFileName: fileName, outputFileName }
}
})
.map((f) => {
const templateFilePath = path.join(
apiBaseTemplatePath,
dir,
f.templateFileName
)
const outputFilePath = path.join(apiSrcPath, dir, f.outputFileName)

return { templateFilePath, outputFilePath }
})

for (const paths of filePaths) {
const content = fs.readFileSync(paths.templateFilePath, 'utf8')

return acc
},
{}
)
filesRecord = {
...filesRecord,
[paths.outputFilePath]: isTypeScriptProject()
? content
: await transformTSToJS(paths.outputFilePath, content),
}
}
}

return filesRecord
}
Expand Down
6 changes: 3 additions & 3 deletions packages/cli-helpers/src/auth/authTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ export const createWebAuth = (basedir: string, webAuthn: boolean) => {

return {
title: `Creating web/src/auth.${ext}`,
task: (ctx: AuthGeneratorCtx) => {
task: async (ctx: AuthGeneratorCtx) => {
// @MARK - finding unused file name here,
// We should only use an unused filename, if the user is CHOOSING not to replace the existing provider

Expand Down Expand Up @@ -399,7 +399,7 @@ export const createWebAuth = (basedir: string, webAuthn: boolean) => {

template = isTSProject
? template
: transformTSToJS(authFileName, template)
: await transformTSToJS(authFileName, template)

fs.writeFileSync(authFileName, template)
},
Expand Down Expand Up @@ -448,7 +448,7 @@ export const generateAuthApiFiles = <Renderer extends typeof ListrRenderer>(

// The keys in `filesRecord` are the full paths to where the file contents,
// which is the values in `filesRecord`, will be written.
let filesRecord = apiSideFiles({ basedir, webAuthn })
let filesRecord = await apiSideFiles({ basedir, webAuthn })

// Always overwrite files in force mode, no need to prompt
let existingFiles: ExistingFiles = 'FAIL'
Expand Down
13 changes: 10 additions & 3 deletions packages/cli-helpers/src/lib/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import path from 'path'

import { vi, test, expect } from 'vitest'

import { prettify } from '../index.js'
Expand All @@ -6,13 +8,16 @@ vi.mock('../paths', () => {
return {
getPaths: () => {
return {
base: '../../../../__fixtures__/example-todo-main',
base: path.resolve(
__dirname,
'../../../../../__fixtures__/example-todo-main'
),
}
},
}
})

test('prettify formats tsx content', () => {
test('prettify formats tsx content', async () => {
const content = `import React from 'react'

interface Props { foo: number, bar: number }
Expand All @@ -30,5 +35,7 @@ test('prettify formats tsx content', () => {

return <>{foo}, {bar}</>}`

expect(prettify('FooBarComponent.template.tsx', content)).toMatchSnapshot()
expect(
await prettify('FooBarComponent.template.tsx', content)
).toMatchSnapshot()
})
14 changes: 9 additions & 5 deletions packages/cli-helpers/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ export const transformTSToJS = (filename: string, content: string) => {
/**
* This returns the config present in `prettier.config.js` of a Redwood project.
*/
export const prettierOptions = () => {
export const getPrettierOptions = async () => {
try {
const options = require(path.join(getPaths().base, 'prettier.config.js'))
const { default: options } = await import(
`file://${path.join(getPaths().base, 'prettier.config.js')}`
)

if (options.tailwindConfig?.startsWith('.')) {
// Make this work with --cwd
Expand All @@ -70,10 +72,10 @@ export const prettierOptions = () => {
}
}

export const prettify = (
export const prettify = async (
templateFilename: string,
renderedTemplate: string
): string => {
): Promise<string> => {
// We format .js and .css templates, we need to tell prettier which parser
// we're using.
// https://prettier.io/docs/en/options.html#parser
Expand All @@ -88,8 +90,10 @@ export const prettify = (
return renderedTemplate
}

const prettierOptions = await getPrettierOptions()

return format(renderedTemplate, {
...prettierOptions(),
...prettierOptions,
parser,
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import '../../../../lib/test'
import { files } from '../../../generate/component/component'
import { tasks } from '../component'

beforeEach(() => {
vol.fromJSON(files({ name: 'About' }))
beforeEach(async () => {
vol.fromJSON(await files({ name: 'About' }))
vi.spyOn(console, 'info').mockImplementation(() => {})
vi.spyOn(console, 'log').mockImplementation(() => {})
})
Expand All @@ -35,15 +35,15 @@ test('destroys component files', async () => {
const t = tasks({ componentName: 'component', filesFn: files, name: 'About' })
t.options.renderer = 'silent'

return t.run().then(() => {
const generatedFiles = Object.keys(files({ name: 'About' }))
return t.run().then(async () => {
const generatedFiles = Object.keys(await files({ name: 'About' }))
expect(generatedFiles.length).toEqual(unlinkSpy.mock.calls.length)
generatedFiles.forEach((f) => expect(unlinkSpy).toHaveBeenCalledWith(f))
})
})

test('destroys component files including stories and tests', async () => {
vol.fromJSON(files({ name: 'About', stories: true, tests: true }))
vol.fromJSON(await files({ name: 'About', stories: true, tests: true }))
const unlinkSpy = vi.spyOn(fs, 'unlinkSync')
const t = tasks({
componentName: 'component',
Expand All @@ -54,9 +54,9 @@ test('destroys component files including stories and tests', async () => {
})
t.options.renderer = 'silent'

return t.run().then(() => {
return t.run().then(async () => {
const generatedFiles = Object.keys(
files({ name: 'About', stories: true, tests: true })
await files({ name: 'About', stories: true, tests: true })
)
expect(generatedFiles.length).toEqual(unlinkSpy.mock.calls.length)
generatedFiles.forEach((f) => expect(unlinkSpy).toHaveBeenCalledWith(f))
Expand Down
8 changes: 5 additions & 3 deletions packages/cli/src/commands/destroy/page/__tests__/page.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import { getPaths } from '../../../../lib'
import { files } from '../../../generate/page/page'
import { tasks } from '../page'

beforeEach(() => {
beforeEach(async () => {
const f = await files({ name: 'About' })
vol.fromJSON({
...files({ name: 'About' }),
...f,
[getPaths().web.routes]: [
'<Routes>',
' <Route path="/about" page={AboutPage} name="about" />',
Expand Down Expand Up @@ -50,8 +51,9 @@ test('destroys page files', async () => {

test('destroys page files with stories and tests', async () => {
const fileOptions = { name: 'About', stories: true, tests: true }
const f = await files(fileOptions)
vol.fromJSON({
...files(fileOptions),
...f,
[getPaths().web.routes]: [
'<Routes>',
' <Route path="/about" page={AboutPage} name="about" />',
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/experimental/setupRscHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export const handler = async ({ force, verbose }) => {

writeFile(
tsconfigPath,
prettify('tsconfig.json', JSON.stringify(tsconfig, null, 2)),
await prettify('tsconfig.json', JSON.stringify(tsconfig, null, 2)),
{
overwriteExisting: true,
}
Expand Down