Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions packages/cli/src/commands/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { recordTelemetryAttributes } from '@cedarjs/cli-helpers'

// @ts-expect-error - Types not available for JS files
import c from '../lib/colors.js'
// @ts-expect-error - Types not available for JS files
import { getPaths } from '../lib/index.js'

export const command = 'check'
export const aliases = ['diagnostics']
Expand All @@ -13,13 +11,12 @@ export const description =
export const handler = async () => {
recordTelemetryAttributes({ command: 'check' })

const { printDiagnostics, DiagnosticSeverity } = (
await import('@cedarjs/structure')
).default

console.log('DiagnosticServerity', DiagnosticSeverity)
const { printDiagnostics } = await import('@cedarjs/structure')
// @ts-expect-error - babel-compiler enum issue. Keeping this as a separate
// import to preserve type information for printDiagnostics
const { DiagnosticSeverity } = (await import('@cedarjs/structure')).default

printDiagnostics(getPaths().base, {
printDiagnostics({
getSeverityLabel: (severity) => {
if (severity === DiagnosticSeverity.Error) {
return c.error('error')
Expand Down
1 change: 1 addition & 0 deletions packages/internal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"@cedarjs/graphql-server": "workspace:*",
"@cedarjs/project-config": "workspace:*",
"@cedarjs/router": "workspace:*",
"@cedarjs/structure": "workspace:*",
"@graphql-codegen/add": "6.0.0",
"@graphql-codegen/cli": "6.2.1",
"@graphql-codegen/client-preset": "5.2.4",
Expand Down
5 changes: 2 additions & 3 deletions packages/internal/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface RouteInformation {
*/
export function getDuplicateRoutes(): RouteInformation[] {
const duplicateRoutes: RouteInformation[] = []
const allRoutes: RWRoute[] = getProject(getPaths().base).router.routes
const allRoutes: RWRoute[] = getProject().router.routes
const uniqueNames = new Set(
allRoutes
.filter((route) => route.name !== undefined)
Expand Down Expand Up @@ -96,8 +96,7 @@ export interface RouteSpec extends RWRouteManifestItem {
}

export const getProjectRoutes = (): RouteSpec[] => {
const rwProject = getProject(getPaths().base)
const routes = rwProject.getRouter().routes
const routes = getProject().getRouter().routes

// @ts-expect-error - relativeFilePath issue. Fixing it is a semver breaking
// change, so I'll leave it as is for now
Expand Down
4 changes: 1 addition & 3 deletions packages/prerender/src/detection/detection.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { getPaths } from '@cedarjs/project-config'
import { getProject } from '@cedarjs/structure'

export const detectPrerenderRoutes = () => {
const rwProject = getProject(getPaths().base)
const routes = rwProject.getRouter().routes
const routes = getProject().getRouter().routes

const prerenderRoutes = routes
.filter((route) => route.prerender) // only select routes with prerender prop
Expand Down
2 changes: 1 addition & 1 deletion packages/structure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The most common use-case is getting the diagnostics of a complete Cedar project:
```ts
import { getProject } from '@cedarjs/structure'
async function test() {
const project = getProject('/path/to/app') // or "file:///path/to/app"
const project = getProject()
for (const d of await project.collectDiagnostics()) {
console.log(d.diagnostic.severity + ': ' + d.diagnostic.message)
}
Expand Down
28 changes: 16 additions & 12 deletions packages/structure/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { getPaths } from '@cedarjs/project-config'

export { DiagnosticSeverity } from './x/diagnostics'
export { RWProject, RWRoute } from './model'
export { URL_file } from './x/URL'
import { RWProject } from './model'
import type { GetSeverityLabelFunction } from './x/diagnostics'
import { ExtendedDiagnostic_format, DiagnosticSeverity } from './x/diagnostics'

export function getProject(projectRoot: string) {
return new RWProject({
projectRoot,
})
export function getProject() {
return new RWProject()
}

export async function printDiagnostics(
projectRoot: string,
opts?: { getSeverityLabel?: GetSeverityLabelFunction },
) {
const project = getProject(projectRoot)
const formatOpts = { cwd: projectRoot, ...opts }
export async function printDiagnostics(opts?: {
getSeverityLabel?: GetSeverityLabelFunction
}) {
const base = getPaths().base
const project = getProject()
const formatOpts = { cwd: base, ...opts }
try {
let warnings = 0
let errors = 0
Expand All @@ -40,7 +40,11 @@ export async function printDiagnostics(
)
process.exit(1)
}
} catch (e: any) {
throw new Error(e.message)
} catch (e) {
if (e instanceof Error) {
throw e
}

throw new Error(String(e))
}
}
2 changes: 1 addition & 1 deletion packages/structure/src/model/RWEnvHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class RWEnvHelper extends BaseNode {
}

private _dotenv(f: string) {
const file = join(this.parent.projectRoot, f)
const file = join(this.parent.pathHelper.base, f)
if (!fs.existsSync(file)) {
return undefined
}
Expand Down
24 changes: 10 additions & 14 deletions packages/structure/src/model/RWProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,17 @@ import { RWSDL } from './RWSDL'
import { RWService } from './RWService'
import { RWTOML } from './RWTOML'

export interface RWProjectOptions {
projectRoot: string
}

const allFilesGlob = '/**/*.{js,jsx,ts,tsx}'

/**
* Represents a Redwood project.
* This is the root node.
*/
export class RWProject extends BaseNode {
constructor(public opts: RWProjectOptions) {
super()
}
parent = undefined

get projectRoot() {
return this.opts.projectRoot
}

@lazy() get id() {
return URL_file(this.projectRoot)
return URL_file(this.pathHelper.base)
}

children() {
Expand All @@ -73,8 +62,9 @@ export class RWProject extends BaseNode {
* Path constants that are relevant to a Redwood project.
*/
@lazy() get pathHelper() {
return getPaths(this.projectRoot)
return getPaths()
}

/**
* Checks for the presence of a tsconfig.json at the root.
*/
Expand All @@ -84,6 +74,7 @@ export class RWProject extends BaseNode {
existsSync(join(this.pathHelper.api.base, 'tsconfig.json'))
)
}

// TODO: do we move this to a separate node? (ex: RWDatabase)
@memo() async prismaDMMF(): Promise<DMMF.Document | undefined> {
try {
Expand All @@ -96,28 +87,33 @@ export class RWProject extends BaseNode {
return undefined
}
}

@memo() async prismaDMMFModelNames() {
const dmmf = await this.prismaDMMF()
if (!dmmf) {
return []
}
return dmmf.datamodel.models.map((m) => m.name)
}

@lazy() get redwoodTOML(): RWTOML {
return new RWTOML(getConfigPath(this.projectRoot), this)
return new RWTOML(getConfigPath(), this)
}

@lazy() private get processPagesDir() {
try {
return processPagesDir(this.pathHelper.web.pages)
} catch {
return []
}
}

@lazy() get pages(): RWPage[] {
return this.processPagesDir.map(
(p) => new RWPage(p.constName, p.path, this),
)
}

@lazy() get router() {
return this.getRouter()
}
Expand Down
2 changes: 1 addition & 1 deletion packages/structure/src/model/RWRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class RWRouter extends FileNode {
*diagnostics() {
if (!this.fileExists) {
// should we assign this error to the project? to cedar.toml?
const uri = URL_file(getConfigPath(this.parent.projectRoot))
const uri = URL_file(getConfigPath())
const message = `Routes.js does not exist`
yield err(uri, message)
// TODO: add quickFix (create a simple Routes.js)
Expand Down
62 changes: 46 additions & 16 deletions packages/structure/src/model/__tests__/model.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
/* eslint-disable @typescript-eslint/no-unused-expressions */
import { basename, resolve } from 'path'

import path from 'node:path'

import { describe, it, expect, beforeAll, afterAll } from 'vitest'

import { URL_file } from '../../x/URL'
import { RWProject } from '../RWProject'

let originalEnv: NodeJS.ProcessEnv
let original_CEDAR_CWD: string | undefined

beforeAll(async () => {
beforeAll(() => {
originalEnv = process.env
process.env.DATABASE_URL = 'file:./dev.db'

original_CEDAR_CWD = process.env.CEDAR_CWD
process.env.CEDAR_CWD = getFixtureDir('example-todo-main')
})

afterAll(() => {
process.env.CEDAR_CWD = original_CEDAR_CWD
process.env = originalEnv
})

describe('Redwood Project Model', () => {
it('can process example-todo-main', async () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()

const pageNames = new Set(project.pages.map((p) => p.basenameNoExt))
expect(pageNames).toEqual(
Expand Down Expand Up @@ -68,7 +76,9 @@ describe('Redwood Project Model', () => {

it('example-todo-main-with-errors', async () => {
const projectRoot = getFixtureDir('example-todo-main-with-errors')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const ds = await project.collectDiagnostics()
expect(ds.length).toBeGreaterThan(0)
// const diagnosticCodes = new Set(ds.map((d) => d.diagnostic.code))
Expand All @@ -83,25 +93,30 @@ describe('Redwood Project Model', () => {
describe('Cells', () => {
it('Correctly determines a Cell component vs a normal component', () => {
const projectRoot = getFixtureDir('example-todo-main-with-errors')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const cells = project.cells
expect(cells).toHaveLength(1)
expect(cells.map((cell) => basename(cell.filePath))).not.toContain(
expect(cells.map((cell) => path.basename(cell.filePath))).not.toContain(
'TableCell.js',
)
})

it('Can get the operation name of the QUERY', () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const cell = project.cells.find((x) => x.uri.endsWith('TodoListCell.tsx'))
expect(cell?.queryOperationName).toMatch('TodoListCell_GetTodos')
})

it('Warns you when you do not supply a name to QUERY', async () => {
const projectRoot = getFixtureDir('example-todo-main-with-errors')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const cell = project.cells.find((x) => x.uri.endsWith('TodoListCell.js'))
const x = await cell?.collectDiagnostics()
expect(x).not.toBeUndefined()
Expand All @@ -114,7 +129,9 @@ describe('Cells', () => {
describe('Redwood Page detection', () => {
it('detects pages', () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const routes = project.getRouter().routes
const pages = routes.map((r) => r.page).sort()
const pageConstants = pages.map((p) => p?.constName)
Expand All @@ -136,7 +153,9 @@ describe('Redwood Page detection', () => {
describe('Redwood Route detection', () => {
it('detects the page identifier for a route', () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const routes = project.getRouter().routes

const pageIdentifiers = routes.map((r) => r.page_identifier_str)
Expand All @@ -146,7 +165,9 @@ describe('Redwood Route detection', () => {
})
it('detects routes with the prerender prop', () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const routes = project.getRouter().routes

const prerenderRoutes = routes
Expand All @@ -172,9 +193,12 @@ describe('Redwood Route detection', () => {
path: '/private-page',
})
})

it('detects authenticated routes', () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const routes = project.getRouter().routes

const authenticatedRoutes = routes
Expand All @@ -191,7 +215,9 @@ describe('Redwood Route detection', () => {

it('detects name and path for an authenticated route', () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const routes = project.getRouter().routes

const authenticatedRoutes = routes
Expand All @@ -212,7 +238,9 @@ describe('Redwood Route detection', () => {

it('detects roles for an authenticated route when roles is a string of a single role', () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const routes = project.getRouter().routes

const authenticatedRoutes = routes
Expand All @@ -233,7 +261,9 @@ describe('Redwood Route detection', () => {

it('detects roles for an authenticated route when roles is an array of a roles', () => {
const projectRoot = getFixtureDir('example-todo-main')
const project = new RWProject({ projectRoot })
process.env.CEDAR_CWD = projectRoot

const project = new RWProject()
const routes = project.getRouter().routes

const authenticatedRoutes = routes
Expand Down Expand Up @@ -262,5 +292,5 @@ function getFixtureDir(
| 'empty-project'
| 'test-project',
) {
return resolve(__dirname, `../../../../../__fixtures__/${name}`)
return path.resolve(__dirname, `../../../../../__fixtures__/${name}`)
}
Loading
Loading