',
)
await page.goto('/')
@@ -63,7 +63,7 @@ test('requireAuth graphql checks', async ({ page }) => {
await expect(
page
.locator('.rw-form-error-title')
- .locator("text=You don't have permission to do that")
+ .locator("text=You don't have permission to do that"),
).toBeTruthy()
await page.goto('/')
diff --git a/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts b/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts
index 280c2ad4c820..3b0450d85523 100644
--- a/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts
+++ b/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts
@@ -68,17 +68,17 @@ test('RBAC: Should not be able to delete contact as non-admin user', async ({
await expect(
page
.locator('.rw-scaffold')
- .locator("text=You don't have permission to do that")
+ .locator("text=You don't have permission to do that"),
).toBeTruthy()
// @NOTE we do this because the scaffold content is actually on the page,
// This is the only way we validate if its actually showing visually
await expect(
- page.locator('.rw-scaffold').locator('text=Contact deleted')
+ page.locator('.rw-scaffold').locator('text=Contact deleted'),
).toBeHidden()
await expect(
- await page.locator('text=charlie@chimichanga.com').count()
+ await page.locator('text=charlie@chimichanga.com').count(),
).toBeGreaterThan(0)
})
@@ -86,7 +86,7 @@ test('RBAC: Admin user should be able to delete contacts', async ({ page }) => {
fs.writeFileSync(
path.join(
process.env.REDWOOD_TEST_PROJECT_PATH as string,
- 'scripts/makeAdmin.ts'
+ 'scripts/makeAdmin.ts',
),
`\
import { db } from 'api/src/lib/db'
@@ -102,7 +102,7 @@ export default async ({ args }) => {
})
console.log(await db.user.findMany())
-}`
+}`,
)
console.log(`Giving ${adminEmail} ADMIN role....`)
@@ -134,11 +134,11 @@ export default async ({ args }) => {
await page.locator('text=Delete').first().click()
await expect(
- page.locator('.rw-scaffold').locator('text=Contact deleted')
+ page.locator('.rw-scaffold').locator('text=Contact deleted'),
).toBeVisible()
await expect(await page.locator('text=charlie@chimichanga.com').count()).toBe(
- contactCountBefore - 1
+ contactCountBefore - 1,
)
})
diff --git a/tasks/smoke-tests/fragments-dev/tests/fragments.spec.ts b/tasks/smoke-tests/fragments-dev/tests/fragments.spec.ts
index 71eee331a19c..8710765d3974 100644
--- a/tasks/smoke-tests/fragments-dev/tests/fragments.spec.ts
+++ b/tasks/smoke-tests/fragments-dev/tests/fragments.spec.ts
@@ -12,6 +12,6 @@ test('Fragments', async ({ page }) => {
const vegetableCard = page.locator('div', { has: lettuceChild })
await expect(vegetableCard.getByText('Vegetable Name: Lettuce')).toBeVisible()
await expect(
- vegetableCard.getByText('Stall Name: Salad Veggies')
+ vegetableCard.getByText('Stall Name: Salad Veggies'),
).toBeVisible()
})
diff --git a/tasks/smoke-tests/fragments-serve/tests/fragments.spec.ts b/tasks/smoke-tests/fragments-serve/tests/fragments.spec.ts
index 71eee331a19c..8710765d3974 100644
--- a/tasks/smoke-tests/fragments-serve/tests/fragments.spec.ts
+++ b/tasks/smoke-tests/fragments-serve/tests/fragments.spec.ts
@@ -12,6 +12,6 @@ test('Fragments', async ({ page }) => {
const vegetableCard = page.locator('div', { has: lettuceChild })
await expect(vegetableCard.getByText('Vegetable Name: Lettuce')).toBeVisible()
await expect(
- vegetableCard.getByText('Stall Name: Salad Veggies')
+ vegetableCard.getByText('Stall Name: Salad Veggies'),
).toBeVisible()
})
diff --git a/tasks/smoke-tests/jsconfig.json b/tasks/smoke-tests/jsconfig.json
index c22c46daceeb..7cc9784f62de 100644
--- a/tasks/smoke-tests/jsconfig.json
+++ b/tasks/smoke-tests/jsconfig.json
@@ -5,6 +5,6 @@
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
- "skipLibCheck": false,
- },
+ "skipLibCheck": false
+ }
}
diff --git a/tasks/smoke-tests/prerender/tests/prerender.spec.ts b/tasks/smoke-tests/prerender/tests/prerender.spec.ts
index 81b9a4bd2a7b..3968468a4247 100644
--- a/tasks/smoke-tests/prerender/tests/prerender.spec.ts
+++ b/tasks/smoke-tests/prerender/tests/prerender.spec.ts
@@ -48,7 +48,7 @@ test('Check that rehydration works for page not wrapped in Set', async ({
// Wait for page to have been rehydrated before getting page content.
// We know the page has been rehydrated when it sends an auth request
await page.waitForResponse((response) =>
- response.url().includes('/.redwood/functions/auth')
+ response.url().includes('/.redwood/functions/auth'),
)
await page.locator('h1').first().waitFor()
@@ -89,7 +89,7 @@ test('Check that rehydration works for page with Cell in Set', async ({
// before getting page content.
// We know cells have started fetching data when we see graphql requests
await page.waitForResponse((response) =>
- response.url().includes('/.redwood/functions/graphql')
+ response.url().includes('/.redwood/functions/graphql'),
)
await page.locator('h2').first().waitFor()
@@ -137,7 +137,7 @@ test('Check that rehydration works for page with code split chunks', async ({
// Wait for page to have been rehydrated before getting page content.
// We know the page has been rehydrated when it sends an auth request
await page.waitForResponse((response) =>
- response.url().includes('/.redwood/functions/auth')
+ response.url().includes('/.redwood/functions/auth'),
)
await expect(page.getByLabel('Name')).toBeVisible()
@@ -190,11 +190,11 @@ test('Check that meta-tags are rendering the correct dynamic data', async () =>
await pageWithoutJs.goto('/blog-post/1')
const metaDescription = await pageWithoutJs.locator(
- 'meta[name="description"]'
+ 'meta[name="description"]',
)
const ogDescription = await pageWithoutJs.locator(
- 'meta[property="og:description"]'
+ 'meta[property="og:description"]',
)
await expect(metaDescription).toHaveAttribute('content', 'Description 1')
await expect(ogDescription).toHaveAttribute('content', 'Description 1')
@@ -217,7 +217,7 @@ test('Check that you can navigate from home page to specific blog post', async (
await pageWithoutJs.goto('/')
const aboutMeAnchor = await pageWithoutJs.locator(
- 'a:has-text("A little more about me")'
+ 'a:has-text("A little more about me")',
)
await aboutMeAnchor.click()
@@ -230,7 +230,7 @@ test('Check that you can navigate from home page to specific blog post', async (
expect(mainContent).not.toMatch(/Welcome to the blog!/)
expect(mainContent).not.toMatch(/What is the meaning of life\?/)
expect(pageWithoutJs.url()).toMatch(
- new RegExp(escapeRegExp(aboutMeAnchorHref))
+ new RegExp(escapeRegExp(aboutMeAnchorHref)),
)
pageWithoutJs.close()
@@ -247,7 +247,7 @@ test('Check that about is prerendered', async () => {
const aboutPageContent = await pageWithoutJs.locator('main').innerText()
expect(aboutPageContent).toBe(
- 'This site was created to demonstrate my mastery of Redwood: Look on my works, ye mighty, and despair!'
+ 'This site was created to demonstrate my mastery of Redwood: Look on my works, ye mighty, and despair!',
)
pageWithoutJs.close()
})
@@ -267,7 +267,7 @@ test('prerender with broken gql query', async () => {
'web',
'src',
'components',
- 'BlogPostsCell'
+ 'BlogPostsCell',
)
const cellPathJs = path.join(cellBasePath, 'BlogPostsCell.jsx')
@@ -284,7 +284,7 @@ test('prerender with broken gql query', async () => {
})
} catch (e) {
expect(e.message).toMatch(
- /GQL error: Cannot query field "timestamp" on type "Post"\./
+ /GQL error: Cannot query field "timestamp" on type "Post"\./,
)
}
diff --git a/tasks/smoke-tests/rsa/tests/rsa.spec.ts b/tasks/smoke-tests/rsa/tests/rsa.spec.ts
index decd1c0b0560..2af276e60af0 100644
--- a/tasks/smoke-tests/rsa/tests/rsa.spec.ts
+++ b/tasks/smoke-tests/rsa/tests/rsa.spec.ts
@@ -14,7 +14,7 @@ test('Submitting the form should return a response', async ({ page }) => {
const submittedPageText = page.locator('#redwood-app > div')
await expect(submittedPageText).toHaveText(
- /The form has been submitted 1 times./
+ /The form has been submitted 1 times./,
)
// Expect an echo of our message back from the server
diff --git a/tasks/smoke-tests/shared/common.ts b/tasks/smoke-tests/shared/common.ts
index b60e75eac343..4631a2395e21 100644
--- a/tasks/smoke-tests/shared/common.ts
+++ b/tasks/smoke-tests/shared/common.ts
@@ -7,18 +7,18 @@ export async function smokeTest({ page }: PlaywrightTestArgs) {
// Check that the blog posts load. We're deliberately not checking their titles because we edit them in other tests.
await expect(
page.getByText(
- 'Meh waistcoat succulents umami asymmetrical, hoodie post-ironic paleo chillwave '
- )
+ 'Meh waistcoat succulents umami asymmetrical, hoodie post-ironic paleo chillwave ',
+ ),
).toBeVisible()
await expect(
page.getByText(
- 'Raclette shoreditch before they sold out lyft. Ethical bicycle rights meh prism '
- )
+ 'Raclette shoreditch before they sold out lyft. Ethical bicycle rights meh prism ',
+ ),
).toBeVisible()
await expect(
page.getByText(
- "I'm baby single- origin coffee kickstarter lo - fi paleo skateboard.Tumblr hasht"
- )
+ "I'm baby single- origin coffee kickstarter lo - fi paleo skateboard.Tumblr hasht",
+ ),
).toBeVisible()
// CSS checks. We saw this break when we switched bundlers, so while it's not comprehensive, it's at least something.
@@ -26,20 +26,20 @@ export async function smokeTest({ page }: PlaywrightTestArgs) {
const bgBlue700 = 'rgb(29, 78, 216)'
expect(page.locator('#redwood-app > header')).toHaveCSS(
'background-color',
- bgBlue700
+ bgBlue700,
)
const textBlue400 = 'rgb(96, 165, 250)'
expect(await page.getByRole('link', { name: 'Redwood Blog' })).toHaveCSS(
'color',
- textBlue400
+ textBlue400,
)
// Check the about page.
await page.getByRole('link', { name: 'About', exact: true }).click()
expect(page.url()).toBe('http://localhost:8910/about')
await page.getByText(
- 'This site was created to demonstrate my mastery of Redwood: Look on my works, ye'
+ 'This site was created to demonstrate my mastery of Redwood: Look on my works, ye',
)
// Check the contact us page.
@@ -79,7 +79,7 @@ export const signUpTestUser = async ({
await Promise.race([
page.waitForURL('/'),
expect(
- page.getByText(`Username \`${email}\` already in use`)
+ page.getByText(`Username \`${email}\` already in use`),
).toBeVisible(),
])
}
diff --git a/tasks/smoke-tests/shared/delayedPage.ts b/tasks/smoke-tests/shared/delayedPage.ts
index 4b52aaf7c85f..fca7c88e08d5 100644
--- a/tasks/smoke-tests/shared/delayedPage.ts
+++ b/tasks/smoke-tests/shared/delayedPage.ts
@@ -3,7 +3,7 @@ import { expect } from '@playwright/test'
export async function checkDelayedPageRendering(
page: Page,
- { expectedDelay }: { expectedDelay: number }
+ { expectedDelay }: { expectedDelay: number },
) {
const delayedLogStatements: { message: string; time: number }[] = []
diff --git a/tasks/smoke-tests/smoke-tests.mjs b/tasks/smoke-tests/smoke-tests.mjs
index 26a76622af3c..131e1cea642c 100644
--- a/tasks/smoke-tests/smoke-tests.mjs
+++ b/tasks/smoke-tests/smoke-tests.mjs
@@ -45,8 +45,8 @@ async function main() {
for (const smokeTest of smokeTests) {
console.log(
`Running ${chalk.magenta(smokeTest)} smoke test against ${chalk.magenta(
- testProjectPath
- )}\n`
+ testProjectPath,
+ )}\n`,
)
await within(async () => {
@@ -83,7 +83,7 @@ async function parseArgs() {
const options = {
testProjectPath: {
description: `Path to the test project. Defaults to the ${chalk.magenta(
- 'REDWOOD_TEST_PROJECT_PATH'
+ 'REDWOOD_TEST_PROJECT_PATH',
)} env var`,
short: 'p',
type: /** @type {const} */ ('string'),
@@ -132,7 +132,7 @@ async function parseArgs() {
'',
getHelp(options),
'',
- ].join('\n')
+ ].join('\n'),
)
}
@@ -154,13 +154,13 @@ async function parseArgs() {
chalk.red('Error: No test project to run smoke tests against.'),
'',
`If you haven't generated a test project, do so first: ${chalk.green(
- 'yarn build:test-project --link '
+ 'yarn build:test-project --link ',
)}.`,
`Then set the ${chalk.magenta(
- 'REDWOOD_TEST_PROJECT_PATH'
+ 'REDWOOD_TEST_PROJECT_PATH',
)} env var to the path of your test project.`,
'',
- ].join('\n')
+ ].join('\n'),
)
}
@@ -178,16 +178,16 @@ async function parseArgs() {
chalk.red("Error: Test project doesn't exist."),
'',
`The test project path you specified (${chalk.magenta(
- testProjectPath
+ testProjectPath,
)}) doesn't exist.`,
`Make sure you've generated a test project: ${chalk.green(
- 'yarn build:test-project --link '
+ 'yarn build:test-project --link ',
)}.`,
`Then set the ${chalk.magenta(
- 'REDWOOD_TEST_PROJECT_PATH'
+ 'REDWOOD_TEST_PROJECT_PATH',
)} env var to the path of your test project.`,
'',
- ].join('\n')
+ ].join('\n'),
)
}
@@ -207,7 +207,7 @@ async function parseArgs() {
.map((dirent) => dirent.name)
const invalidSmokeTest = smokeTests.find(
- (smokeTest) => !availableSmokeTests.includes(smokeTest)
+ (smokeTest) => !availableSmokeTests.includes(smokeTest),
)
// Error if the user passed an invalid smoke test.
@@ -223,7 +223,7 @@ async function parseArgs() {
'',
getHelp(options),
'',
- ].join('\n')
+ ].join('\n'),
)
}
@@ -234,8 +234,8 @@ async function parseArgs() {
if (!isProjectSyncRunning) {
console.warn(
chalk.yellow(
- 'Warning: If you want to test against the framework, you must have `yarn rwfw project:sync` running in your test project.'
- )
+ 'Warning: If you want to test against the framework, you must have `yarn rwfw project:sync` running in your test project.',
+ ),
)
}
@@ -276,7 +276,7 @@ async function parseArgs() {
process.exitCode = 1
throw new Error()
},
- }
+ },
)
smokeTests = answer.smokeTests
@@ -289,7 +289,7 @@ async function parseArgs() {
if (
smokeTests.some((smokeTest) =>
- ['prerender', 'serve'].includes(smokeTest)
+ ['prerender', 'serve'].includes(smokeTest),
) &&
!isTestProjectBuilt
) {
@@ -297,13 +297,13 @@ async function parseArgs() {
throw new Error(
[
chalk.red(
- 'Error: You must build the test project before running the prerender or serve smoke tests.'
+ 'Error: You must build the test project before running the prerender or serve smoke tests.',
),
'',
chalk.green(` cd ${testProjectPath}`),
chalk.green(` yarn rw build`),
'',
- ].join('\n')
+ ].join('\n'),
)
}
@@ -318,7 +318,7 @@ function getHelp(options) {
// Find the length of the longest option and justify the text based on it.
const longestOptionLength = Object.entries(options).reduce(
(max, [name]) => Math.max(max, name.length),
- 0
+ 0,
)
const justifiedOptions = Object.entries(options).map(([name, config]) => {
@@ -343,10 +343,10 @@ function getHelp(options) {
chalk.green(' yarn smoke-tests [options] [smoke-test..]'),
'',
`Make sure you've got a test project. (You can make one via ${chalk.green(
- 'yarn build:test-project --link '
+ 'yarn build:test-project --link ',
)}.)`,
`Then set the ${chalk.magenta(
- 'REDWOOD_TEST_PROJECT_PATH'
+ 'REDWOOD_TEST_PROJECT_PATH',
)} env var to the path of your test project.`,
'',
chalk.dim(' # Let this script prompt you for which smoke test to run'),
@@ -356,10 +356,10 @@ function getHelp(options) {
chalk.cyan(' REDWOOD_TEST_PROJECT_PATH= yarn smoke-tests dev'),
'',
chalk.dim(
- ' # Pass flags to `npx playwright test` (see `npx playwright test --help`)'
+ ' # Pass flags to `npx playwright test` (see `npx playwright test --help`)',
),
chalk.cyan(
- ' REDWOOD_TEST_PROJECT_PATH= yarn smoke-tests --playwrightOptions="--debug"'
+ ' REDWOOD_TEST_PROJECT_PATH= yarn smoke-tests --playwrightOptions="--debug"',
),
'',
chalk.bold('## Options'),
diff --git a/tasks/smoke-tests/streaming-ssr-dev/tests/progressiveRendering.spec.ts b/tasks/smoke-tests/streaming-ssr-dev/tests/progressiveRendering.spec.ts
index 0f630a02bae9..b657e531fae6 100644
--- a/tasks/smoke-tests/streaming-ssr-dev/tests/progressiveRendering.spec.ts
+++ b/tasks/smoke-tests/streaming-ssr-dev/tests/progressiveRendering.spec.ts
@@ -71,15 +71,15 @@ test('Check delayed page has content progressively rendered', async () => {
// Check that its actually rendered on the page. Important when **not** progressively rendering
await expect(
- pageWithClientBlocked.locator('[data-test-id="delayed-text-1"]')
+ pageWithClientBlocked.locator('[data-test-id="delayed-text-1"]'),
).toHaveCount(1)
await expect(
- pageWithClientBlocked.locator('[data-test-id="delayed-text-2"]')
+ pageWithClientBlocked.locator('[data-test-id="delayed-text-2"]'),
).toHaveCount(1)
await expect(
- pageWithClientBlocked.locator('[data-test-id="delayed-text-3"]')
+ pageWithClientBlocked.locator('[data-test-id="delayed-text-3"]'),
).toHaveCount(1)
await expect(
- pageWithClientBlocked.locator('[data-test-id="delayed-text-4"]')
+ pageWithClientBlocked.locator('[data-test-id="delayed-text-4"]'),
).toHaveCount(1)
})
diff --git a/tasks/smoke-tests/streaming-ssr-prod/tests/botRendering.spec.ts b/tasks/smoke-tests/streaming-ssr-prod/tests/botRendering.spec.ts
index 59619645f3bc..9ff3707793f7 100644
--- a/tasks/smoke-tests/streaming-ssr-prod/tests/botRendering.spec.ts
+++ b/tasks/smoke-tests/streaming-ssr-prod/tests/botRendering.spec.ts
@@ -40,7 +40,7 @@ test('Check delayed page is NOT progressively rendered', async ({
const botPageNoBundle = await botContext.newPage()
await botPageNoBundle.route('**/*.*.{js,tsx,ts,jsx}', (route) =>
- route.abort()
+ route.abort(),
)
await checkDelayedPageRendering(botPageNoBundle, {
diff --git a/tasks/test-project/codemods/Redwood.stories.mdx b/tasks/test-project/codemods/Redwood.stories.mdx
index 4481bff2fdc9..d0ef9e47cfcb 100644
--- a/tasks/test-project/codemods/Redwood.stories.mdx
+++ b/tasks/test-project/codemods/Redwood.stories.mdx
@@ -3,7 +3,10 @@ import { Meta } from '@storybook/addon-docs'
-
+
Redwood
diff --git a/tasks/test-project/codemods/authorCell.js b/tasks/test-project/codemods/authorCell.js
index 6747d7015cc9..210a8c9d75c2 100644
--- a/tasks/test-project/codemods/authorCell.js
+++ b/tasks/test-project/codemods/authorCell.js
@@ -16,7 +16,7 @@ export default (file, api) => {
const componentImport = j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('Author'))],
- j.stringLiteral('src/components/Author')
+ j.stringLiteral('src/components/Author'),
)
root.find(j.ExportNamedDeclaration).at(0).insertBefore(componentImport)
@@ -32,7 +32,7 @@ export default (file, api) => {
const { node } = nodePath
node.init.quasi = j.templateLiteral(
[j.templateElement({ raw: query, cooked: query }, true)],
- []
+ [],
)
return node
})
diff --git a/tasks/test-project/codemods/blogLayout.js b/tasks/test-project/codemods/blogLayout.js
index 596cfb0fe5db..39c8fc0460d9 100644
--- a/tasks/test-project/codemods/blogLayout.js
+++ b/tasks/test-project/codemods/blogLayout.js
@@ -84,12 +84,12 @@ export default (file, api) => {
j.importSpecifier(j.identifier('NavLink'), j.identifier('NavLink')),
j.importSpecifier(j.identifier('routes'), j.identifier('routes')),
],
- j.stringLiteral('@redwoodjs/router')
+ j.stringLiteral('@redwoodjs/router'),
)
const authImport = j.importDeclaration(
[j.importSpecifier(j.identifier('useAuth'), j.identifier('useAuth'))],
- j.stringLiteral('src/auth')
+ j.stringLiteral('src/auth'),
)
root.find(j.VariableDeclaration).insertBefore(routerImport)
diff --git a/tasks/test-project/codemods/blogPost.js b/tasks/test-project/codemods/blogPost.js
index 1e6a29bc75f1..be868b08915b 100644
--- a/tasks/test-project/codemods/blogPost.js
+++ b/tasks/test-project/codemods/blogPost.js
@@ -44,7 +44,7 @@ export default (file, api) => {
j.importSpecifier(j.identifier('Link'), j.identifier('Link')),
j.importSpecifier(j.identifier('routes'), j.identifier('routes')),
],
- j.stringLiteral('@redwoodjs/router')
+ j.stringLiteral('@redwoodjs/router'),
)
root.find(j.VariableDeclaration).insertBefore(routerImport)
diff --git a/tasks/test-project/codemods/blogPostCell.js b/tasks/test-project/codemods/blogPostCell.js
index b05bc6d8eb42..3345d7e2a7a1 100644
--- a/tasks/test-project/codemods/blogPostCell.js
+++ b/tasks/test-project/codemods/blogPostCell.js
@@ -20,7 +20,7 @@ export default (file, api) => {
const componentImport = j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('BlogPost'))],
- j.stringLiteral('src/components/BlogPost')
+ j.stringLiteral('src/components/BlogPost'),
)
root.find(j.ExportNamedDeclaration).at(0).insertBefore(componentImport)
@@ -36,7 +36,7 @@ export default (file, api) => {
const { node } = nodePath
node.init.quasi = j.templateLiteral(
[j.templateElement({ raw: query, cooked: query }, true)],
- []
+ [],
)
return node
})
diff --git a/tasks/test-project/codemods/blogPostsCell.js b/tasks/test-project/codemods/blogPostsCell.js
index d3d84a9eddb7..1efbe6624db0 100644
--- a/tasks/test-project/codemods/blogPostsCell.js
+++ b/tasks/test-project/codemods/blogPostsCell.js
@@ -22,7 +22,7 @@ export default (file, api) => {
const importComponent = j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('BlogPost'))],
- j.stringLiteral('src/components/BlogPost')
+ j.stringLiteral('src/components/BlogPost'),
)
root.find(j.ExportNamedDeclaration).at(0).insertBefore(importComponent)
@@ -38,7 +38,7 @@ export default (file, api) => {
const { node } = nodePath
node.init.quasi = j.templateLiteral(
[j.templateElement({ raw: query, cooked: query }, true)],
- []
+ [],
)
return node
})
diff --git a/tasks/test-project/codemods/contactsSdl.js b/tasks/test-project/codemods/contactsSdl.js
index a04e6d2f6380..9b1c30d257fc 100644
--- a/tasks/test-project/codemods/contactsSdl.js
+++ b/tasks/test-project/codemods/contactsSdl.js
@@ -20,7 +20,7 @@ export default (file, api) => {
const schema = `${node.init.quasi.quasis[0].value.raw} ${mutation}`
node.init.quasi = j.templateLiteral(
[j.templateElement({ raw: schema, cooked: schema }, true)],
- []
+ [],
)
return node
})
diff --git a/tasks/test-project/codemods/delayedPage.js b/tasks/test-project/codemods/delayedPage.js
index 96f3487a1452..e08b46981946 100644
--- a/tasks/test-project/codemods/delayedPage.js
+++ b/tasks/test-project/codemods/delayedPage.js
@@ -83,14 +83,14 @@ export default (file, api) => {
const serverInjectImport = j.importDeclaration(
[j.importSpecifier(j.identifier('useServerInsertedHTML'))],
- j.stringLiteral('@redwoodjs/web')
+ j.stringLiteral('@redwoodjs/web'),
)
const reactImports = j.importDeclaration(
['Suspense', 'useState', 'use', 'useRef'].map((importName) =>
- j.importSpecifier(j.identifier(importName))
+ j.importSpecifier(j.identifier(importName)),
),
- j.stringLiteral('react')
+ j.stringLiteral('react'),
)
root
diff --git a/tasks/test-project/codemods/groceriesPage.ts b/tasks/test-project/codemods/groceriesPage.ts
index 802c2a930ca7..84d59cb14bc0 100644
--- a/tasks/test-project/codemods/groceriesPage.ts
+++ b/tasks/test-project/codemods/groceriesPage.ts
@@ -52,8 +52,8 @@ export default (file: FileInfo, api: API) => {
j.importSpecifier(j.identifier('GetProduce')),
],
j.stringLiteral('types/graphql'),
- 'type'
- )
+ 'type',
+ ),
)
// Replace
@@ -90,15 +90,15 @@ export default (file: FileInfo, api: API) => {
return [
j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('FruitInfo'))],
- j.stringLiteral('src/components/FruitInfo')
+ j.stringLiteral('src/components/FruitInfo'),
),
j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('ProduceInfo'))],
- j.stringLiteral('src/components/ProduceInfo')
+ j.stringLiteral('src/components/ProduceInfo'),
),
j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('VegetableInfo'))],
- j.stringLiteral('src/components/VegetableInfo')
+ j.stringLiteral('src/components/VegetableInfo'),
),
]
})
@@ -137,9 +137,9 @@ export default (file: FileInfo, api: API) => {
j.identifier('gql'),
j.templateLiteral(
[j.templateElement({ raw: query, cooked: query }, true)],
- []
- )
- )
+ [],
+ ),
+ ),
),
])
})
@@ -183,12 +183,12 @@ export default (file: FileInfo, api: API) => {
[
j.templateElement(
{ raw: produceQuery, cooked: produceQuery },
- true
+ true,
),
],
- []
- )
- )
+ [],
+ ),
+ ),
),
])
})
diff --git a/tasks/test-project/codemods/routes.js b/tasks/test-project/codemods/routes.js
index 0133f4ec5d0e..fa49da362d4b 100644
--- a/tasks/test-project/codemods/routes.js
+++ b/tasks/test-project/codemods/routes.js
@@ -7,12 +7,12 @@ export default (file, api) => {
const blogImport = j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('BlogLayout'))],
- j.stringLiteral('src/layouts/BlogLayout')
+ j.stringLiteral('src/layouts/BlogLayout'),
)
const homePageImport = j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('HomePage'))],
- j.stringLiteral('src/pages/HomePage')
+ j.stringLiteral('src/pages/HomePage'),
)
root
@@ -42,11 +42,11 @@ export default (file, api) => {
j.jsxOpeningElement(j.jsxIdentifier('Set'), [
j.jsxAttribute(
j.jsxIdentifier('wrap'),
- j.jsxExpressionContainer(j.identifier('BlogLayout'))
+ j.jsxExpressionContainer(j.identifier('BlogLayout')),
),
]),
j.jsxClosingElement(j.jsxIdentifier('Set')),
- [...node.children]
+ [...node.children],
),
]
@@ -60,7 +60,7 @@ export default (file, api) => {
j.filters.JSXElement.hasAttributes({
name: 'profile',
path: '/profile',
- })
+ }),
)
.at(0)
.replaceWith((nodePath) => {
@@ -70,11 +70,11 @@ export default (file, api) => {
j.jsxOpeningElement(j.jsxIdentifier('Private'), [
j.jsxAttribute(
j.jsxIdentifier('unauthenticated'),
- j.literal('login')
+ j.literal('login'),
),
]),
j.jsxClosingElement(j.jsxIdentifier('Private')),
- [node]
+ [node],
)
return privateSetWrapped
diff --git a/tasks/test-project/codemods/updateAuthorStories.js b/tasks/test-project/codemods/updateAuthorStories.js
index 96efd2db365c..cf32f1cf1d95 100644
--- a/tasks/test-project/codemods/updateAuthorStories.js
+++ b/tasks/test-project/codemods/updateAuthorStories.js
@@ -33,10 +33,10 @@ export default (file, api) => {
j.property(
'init',
j.identifier('email'),
- j.literal('story.user@email.com')
+ j.literal('story.user@email.com'),
),
j.property('init', j.identifier('fullName'), j.literal('Story User')),
- ])
+ ]),
),
])
@@ -47,7 +47,7 @@ export default (file, api) => {
// }
const primaryIdentifier = j.identifier('Primary')
primaryIdentifier.typeAnnotation = j.tsTypeAnnotation(
- j.tsTypeReference(j.identifier('Story'), null)
+ j.tsTypeReference(j.identifier('Story'), null),
)
const primaryWithRender = j.exportNamedDeclaration(
@@ -68,22 +68,22 @@ export default (file, api) => {
[
j.jsxAttribute(
j.jsxIdentifier('author'),
- j.jsxExpressionContainer(j.identifier('author'))
+ j.jsxExpressionContainer(j.identifier('author')),
),
],
- true
+ true,
),
null,
[],
- true
- )
+ true,
+ ),
),
- ])
- )
+ ]),
+ ),
),
- ])
+ ]),
),
- ])
+ ]),
)
if (exportStatement.length > 0) {
diff --git a/tasks/test-project/codemods/updateAuthorTest.js b/tasks/test-project/codemods/updateAuthorTest.js
index d2006fa6ec13..a1c79fd734fa 100644
--- a/tasks/test-project/codemods/updateAuthorTest.js
+++ b/tasks/test-project/codemods/updateAuthorTest.js
@@ -10,24 +10,28 @@ export default (file, api) => {
j.variableDeclarator(
j.identifier('author'),
j.objectExpression([
- j.property('init', j.identifier('email'), j.literal('test.user@email.com')),
+ j.property(
+ 'init',
+ j.identifier('email'),
+ j.literal('test.user@email.com'),
+ ),
j.property('init', j.identifier('fullName'), j.literal('Test User')),
- ])
- )
+ ]),
+ ),
])
root.find(j.ExpressionStatement).at(0).insertBefore(authorDeclaration)
// Change `` to ``
root
- .find(j.JSXOpeningElement, { name: { name: 'Author' } } )
+ .find(j.JSXOpeningElement, { name: { name: 'Author' } })
.replaceWith((nodePath) => {
const { node } = nodePath
node.attributes.push(
j.jsxAttribute(
j.jsxIdentifier('author'),
- j.jsxExpressionContainer(j.identifier('author'))
- )
+ j.jsxExpressionContainer(j.identifier('author')),
+ ),
)
return node
diff --git a/tasks/test-project/codemods/updateBlogPostPageStories.js b/tasks/test-project/codemods/updateBlogPostPageStories.js
index eada95b8d15f..d293ae119380 100644
--- a/tasks/test-project/codemods/updateBlogPostPageStories.js
+++ b/tasks/test-project/codemods/updateBlogPostPageStories.js
@@ -26,7 +26,7 @@ export default (file, api) => {
const primaryIdentifier = j.identifier('Primary')
// Add the `Story` type annotation
primaryIdentifier.typeAnnotation = j.tsTypeAnnotation(
- j.tsTypeReference(j.identifier('Story'), null)
+ j.tsTypeReference(j.identifier('Story'), null),
)
// export const Primary: Story = {
@@ -52,23 +52,23 @@ export default (file, api) => {
[
j.jsxAttribute(
j.jsxIdentifier('id'),
- j.jsxExpressionContainer(j.numericLiteral(42))
+ j.jsxExpressionContainer(j.numericLiteral(42)),
),
j.jsxSpreadAttribute(j.identifier('args')),
],
- true
+ true,
),
null,
[],
- true
- )
+ true,
+ ),
),
- ])
- )
+ ]),
+ ),
),
- ])
+ ]),
),
- ])
+ ]),
)
if (exportStatement.length > 0) {
diff --git a/tasks/test-project/codemods/updateWaterfallPageStories.js b/tasks/test-project/codemods/updateWaterfallPageStories.js
index 99b646980f80..97252516efd6 100644
--- a/tasks/test-project/codemods/updateWaterfallPageStories.js
+++ b/tasks/test-project/codemods/updateWaterfallPageStories.js
@@ -26,7 +26,7 @@ export default (file, api) => {
const primaryIdentifier = j.identifier('Primary')
// Add the `Story` type annotation
primaryIdentifier.typeAnnotation = j.tsTypeAnnotation(
- j.tsTypeReference(j.identifier('Story'), null)
+ j.tsTypeReference(j.identifier('Story'), null),
)
// export const Primary: Story = {
@@ -52,23 +52,23 @@ export default (file, api) => {
[
j.jsxAttribute(
j.jsxIdentifier('id'),
- j.jsxExpressionContainer(j.numericLiteral(42))
+ j.jsxExpressionContainer(j.numericLiteral(42)),
),
j.jsxSpreadAttribute(j.identifier('args')),
],
- true
+ true,
),
null,
[],
- true
- )
+ true,
+ ),
),
- ])
- )
+ ]),
+ ),
),
- ])
+ ]),
),
- ])
+ ]),
)
if (exportStatement.length > 0) {
diff --git a/tasks/test-project/codemods/usersSdl.js b/tasks/test-project/codemods/usersSdl.js
index 8761452ea4e5..5ea2532abb12 100644
--- a/tasks/test-project/codemods/usersSdl.js
+++ b/tasks/test-project/codemods/usersSdl.js
@@ -36,7 +36,7 @@ export default (file, api) => {
node.init.quasi = j.templateLiteral(
[j.templateElement({ raw: schema, cooked: schema }, true)],
- []
+ [],
)
return node
})
diff --git a/tasks/test-project/codemods/usersService.js b/tasks/test-project/codemods/usersService.js
index 2b5cfb412028..b5f0f163a0da 100644
--- a/tasks/test-project/codemods/usersService.js
+++ b/tasks/test-project/codemods/usersService.js
@@ -14,21 +14,23 @@ export default (file, api) => {
// const params = j.objectPattern([property])
- return root
- .find(j.VariableDeclarator, {
- id: {
- type: 'Identifier',
- name: 'users',
- },
- })
- // .replaceWith((nodePath) => {
- // const { node } = nodePath
- // node.id.name = 'user'
- // node.id.typeAnnotation.typeAnnotation.indexType.literal.value = 'user'
- // node.init.params[0] = params
- // node.init.body.body[0] = body
- // return node
- // })
- .remove()
- .toSource()
+ return (
+ root
+ .find(j.VariableDeclarator, {
+ id: {
+ type: 'Identifier',
+ name: 'users',
+ },
+ })
+ // .replaceWith((nodePath) => {
+ // const { node } = nodePath
+ // node.id.name = 'user'
+ // node.id.typeAnnotation.typeAnnotation.indexType.literal.value = 'user'
+ // node.init.params[0] = params
+ // node.init.body.body[0] = body
+ // return node
+ // })
+ .remove()
+ .toSource()
+ )
}
diff --git a/tasks/test-project/codemods/waterfallBlogPostCell.js b/tasks/test-project/codemods/waterfallBlogPostCell.js
index 87e88fa70f5b..80e126f9d64d 100644
--- a/tasks/test-project/codemods/waterfallBlogPostCell.js
+++ b/tasks/test-project/codemods/waterfallBlogPostCell.js
@@ -40,7 +40,7 @@ export default (file, api) => {
const componentImport = j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('AuthorCell'))],
- j.stringLiteral('src/components/AuthorCell')
+ j.stringLiteral('src/components/AuthorCell'),
)
root.find(j.ExportNamedDeclaration).at(0).insertBefore(componentImport)
@@ -56,7 +56,7 @@ export default (file, api) => {
const { node } = nodePath
node.init.quasi = j.templateLiteral(
[j.templateElement({ raw: query, cooked: query }, true)],
- []
+ [],
)
return node
})
diff --git a/tasks/test-project/codemods/waterfallPage.js b/tasks/test-project/codemods/waterfallPage.js
index 1664ff9ce591..07281f0ca1b8 100644
--- a/tasks/test-project/codemods/waterfallPage.js
+++ b/tasks/test-project/codemods/waterfallPage.js
@@ -8,7 +8,7 @@ export default (file, api) => {
const importComponent = j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('WaterfallBlogPostCell'))],
- j.stringLiteral('src/components/WaterfallBlogPostCell')
+ j.stringLiteral('src/components/WaterfallBlogPostCell'),
)
root.find(j.ImportDeclaration).at(-1).insertAfter(importComponent)
diff --git a/tasks/test-project/rebuild-fragments-test-project-fixture.ts b/tasks/test-project/rebuild-fragments-test-project-fixture.ts
index acd94ef453b2..939161fc32fc 100755
--- a/tasks/test-project/rebuild-fragments-test-project-fixture.ts
+++ b/tasks/test-project/rebuild-fragments-test-project-fixture.ts
@@ -56,7 +56,7 @@ const OUTPUT_PROJECT_PATH = resumePath
os.tmpdir(),
'redwood-fragment-test-project',
// ":" is problematic with paths
- new Date().toISOString().split(':').join('-')
+ new Date().toISOString().split(':').join('-'),
)
let startStep = resumeStep || ''
@@ -66,7 +66,7 @@ if (!startStep) {
try {
const stepTxt = fs.readFileSync(
path.join(OUTPUT_PROJECT_PATH, 'step.txt'),
- 'utf-8'
+ 'utf-8',
)
if (stepTxt) {
@@ -137,12 +137,12 @@ async function tuiTask({ step, title, content, task, parent }: TuiTaskDef) {
if (e instanceof ExecaError) {
tui.displayError(
'Failed ' + title.toLowerCase().replace('...', ''),
- 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr
+ 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr,
)
} else {
tui.displayError(
'Failed ' + title.toLowerCase().replace('...', ''),
- e.message
+ e.message,
)
}
@@ -158,12 +158,12 @@ async function tuiTask({ step, title, content, task, parent }: TuiTaskDef) {
if (e instanceof ExecaError) {
tui.displayError(
'Failed ' + title.toLowerCase().replace('...', ''),
- 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr
+ 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr,
)
} else {
tui.displayError(
'Failed ' + title.toLowerCase().replace('...', ''),
- e.message
+ e.message,
)
}
@@ -218,8 +218,8 @@ if (resume) {
console.error(
chalk.red.bold(
'\n`resume` option is not supported yet. ' +
- 'Please use `resumePath` instead.\n'
- )
+ 'Please use `resumePath` instead.\n',
+ ),
)
process.exit(1)
@@ -230,8 +230,8 @@ if (resumePath && !fs.existsSync(path.join(resumePath, 'redwood.toml'))) {
chalk.red.bold(
`
No redwood.toml file found at the given path: ${resumePath}
- `
- )
+ `,
+ ),
)
process.exit(1)
}
@@ -243,7 +243,7 @@ const createProject = () => {
cmd,
// We create a ts project and convert using ts-to-js at the end if typescript flag is false
['--no-yarn-install', '--typescript', '--overwrite', '--no-git'],
- getExecaOptions(RW_FRAMEWORK_PATH)
+ getExecaOptions(RW_FRAMEWORK_PATH),
)
return subprocess
@@ -252,7 +252,7 @@ const createProject = () => {
const copyProject = async () => {
const fixturePath = path.join(
RW_FRAMEWORK_PATH,
- '__fixtures__/fragment-test-project'
+ '__fixtures__/fragment-test-project',
)
// remove existing Fixture
@@ -290,7 +290,7 @@ async function runCommand() {
return exec(
'yarn build:clean && yarn build',
[],
- getExecaOptions(RW_FRAMEWORK_PATH)
+ getExecaOptions(RW_FRAMEWORK_PATH),
)
},
})
@@ -303,7 +303,7 @@ async function runCommand() {
return addFrameworkDepsToProject(
RW_FRAMEWORK_PATH,
OUTPUT_PROJECT_PATH,
- 'pipe' // TODO: Remove this when everything is using @rwjs/tui
+ 'pipe', // TODO: Remove this when everything is using @rwjs/tui
)
},
})
@@ -331,12 +331,12 @@ async function runCommand() {
newRedwoodToml = newRedwoodToml.replace(
/\port = 8910/,
- 'port = "${WEB_DEV_PORT:8910}"'
+ 'port = "${WEB_DEV_PORT:8910}"',
)
newRedwoodToml = newRedwoodToml.replace(
/\port = 8911/,
- 'port = "${API_DEV_PORT:8911}"'
+ 'port = "${API_DEV_PORT:8911}"',
)
fs.writeFileSync(REDWOOD_TOML_PATH, newRedwoodToml)
@@ -350,7 +350,7 @@ async function runCommand() {
return copyFrameworkPackages(
RW_FRAMEWORK_PATH,
OUTPUT_PROJECT_PATH,
- 'pipe'
+ 'pipe',
)
},
})
@@ -396,7 +396,7 @@ async function runCommand() {
return exec(
'yarn rw prisma migrate reset',
['--force'],
- getExecaOptions(OUTPUT_PROJECT_PATH)
+ getExecaOptions(OUTPUT_PROJECT_PATH),
)
},
})
@@ -467,9 +467,9 @@ async function runCommand() {
fs.copyFileSync(
path.join(
__dirname,
- '../../packages/create-redwood-app/templates/ts/package.json'
+ '../../packages/create-redwood-app/templates/ts/package.json',
),
- path.join(OUTPUT_PROJECT_PATH, 'package.json')
+ path.join(OUTPUT_PROJECT_PATH, 'package.json'),
)
// removes existing Fixture and replaces with newly built project,
diff --git a/tasks/test-project/rebuild-test-project-fixture.ts b/tasks/test-project/rebuild-test-project-fixture.ts
index 581f20a1dddb..b7e66f932ea8 100755
--- a/tasks/test-project/rebuild-test-project-fixture.ts
+++ b/tasks/test-project/rebuild-test-project-fixture.ts
@@ -56,7 +56,7 @@ const OUTPUT_PROJECT_PATH = resumePath
os.tmpdir(),
'redwood-test-project',
// ":" is problematic with paths
- new Date().toISOString().split(':').join('-')
+ new Date().toISOString().split(':').join('-'),
)
let startStep = resumeStep || ''
@@ -66,7 +66,7 @@ if (!startStep) {
try {
const stepTxt = fs.readFileSync(
path.join(OUTPUT_PROJECT_PATH, 'step.txt'),
- 'utf-8'
+ 'utf-8',
)
if (stepTxt) {
@@ -137,12 +137,12 @@ async function tuiTask({ step, title, content, task, parent }: TuiTaskDef) {
if (e instanceof ExecaError) {
tui.displayError(
'Failed ' + title.toLowerCase().replace('...', ''),
- 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr
+ 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr,
)
} else {
tui.displayError(
'Failed ' + title.toLowerCase().replace('...', ''),
- e.message
+ e.message,
)
}
@@ -158,12 +158,12 @@ async function tuiTask({ step, title, content, task, parent }: TuiTaskDef) {
if (e instanceof ExecaError) {
tui.displayError(
'Failed ' + title.toLowerCase().replace('...', ''),
- 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr
+ 'stdout:\n' + e.stdout + '\n\n' + 'stderr:\n' + e.stderr,
)
} else {
tui.displayError(
'Failed ' + title.toLowerCase().replace('...', ''),
- e.message
+ e.message,
)
}
@@ -218,8 +218,8 @@ if (resume) {
console.error(
chalk.red.bold(
'\n`resume` option is not supported yet. ' +
- 'Please use `resumePath` instead.\n'
- )
+ 'Please use `resumePath` instead.\n',
+ ),
)
process.exit(1)
@@ -230,8 +230,8 @@ if (resumePath && !fs.existsSync(path.join(resumePath, 'redwood.toml'))) {
chalk.red.bold(
`
No redwood.toml file found at the given path: ${resumePath}
- `
- )
+ `,
+ ),
)
process.exit(1)
}
@@ -243,7 +243,7 @@ const createProject = () => {
cmd,
// We create a ts project and convert using ts-to-js at the end if typescript flag is false
['--no-yarn-install', '--typescript', '--overwrite', '--no-git'],
- getExecaOptions(RW_FRAMEWORK_PATH)
+ getExecaOptions(RW_FRAMEWORK_PATH),
)
return subprocess
@@ -287,7 +287,7 @@ async function runCommand() {
return exec(
'yarn build:clean && yarn build',
[],
- getExecaOptions(RW_FRAMEWORK_PATH)
+ getExecaOptions(RW_FRAMEWORK_PATH),
)
},
})
@@ -300,7 +300,7 @@ async function runCommand() {
return addFrameworkDepsToProject(
RW_FRAMEWORK_PATH,
OUTPUT_PROJECT_PATH,
- 'pipe' // TODO: Remove this when everything is using @rwjs/tui
+ 'pipe', // TODO: Remove this when everything is using @rwjs/tui
)
},
})
@@ -328,12 +328,12 @@ async function runCommand() {
newRedwoodToml = newRedwoodToml.replace(
/\port = 8910/,
- 'port = "${WEB_DEV_PORT:8910}"'
+ 'port = "${WEB_DEV_PORT:8910}"',
)
newRedwoodToml = newRedwoodToml.replace(
/\port = 8911/,
- 'port = "${API_DEV_PORT:8911}"'
+ 'port = "${API_DEV_PORT:8911}"',
)
fs.writeFileSync(REDWOOD_TOML_PATH, newRedwoodToml)
@@ -347,7 +347,7 @@ async function runCommand() {
return copyFrameworkPackages(
RW_FRAMEWORK_PATH,
OUTPUT_PROJECT_PATH,
- 'pipe'
+ 'pipe',
)
},
})
@@ -386,26 +386,21 @@ async function runCommand() {
},
})
- await tuiTask({
- step: 9,
- title: 'Add scripts',
- task: () => {
- const nestedPath = path.join(
- OUTPUT_PROJECT_PATH,
- 'scripts',
- 'one',
- 'two'
- )
-
- fs.mkdirSync(nestedPath, { recursive: true })
- fs.writeFileSync(
- path.join(nestedPath, 'myNestedScript.ts'),
- 'export default async () => {\n' +
- " console.log('Hello from myNestedScript.ts')\n" +
- '}\n\n'
- )
- },
- })
+ await tuiTask({
+ step: 9,
+ title: 'Add scripts',
+ task: () => {
+ const nestedPath = path.join(OUTPUT_PROJECT_PATH, 'scripts', 'one', 'two')
+
+ fs.mkdirSync(nestedPath, { recursive: true })
+ fs.writeFileSync(
+ path.join(nestedPath, 'myNestedScript.ts'),
+ 'export default async () => {\n' +
+ " console.log('Hello from myNestedScript.ts')\n" +
+ '}\n\n',
+ )
+ },
+ })
await tuiTask({
step: 10,
@@ -414,7 +409,7 @@ async function runCommand() {
return exec(
'yarn rw prisma migrate reset',
['--force'],
- getExecaOptions(OUTPUT_PROJECT_PATH)
+ getExecaOptions(OUTPUT_PROJECT_PATH),
)
},
})
@@ -478,9 +473,9 @@ async function runCommand() {
fs.copyFileSync(
path.join(
__dirname,
- '../../packages/create-redwood-app/templates/ts/package.json'
+ '../../packages/create-redwood-app/templates/ts/package.json',
),
- path.join(OUTPUT_PROJECT_PATH, 'package.json')
+ path.join(OUTPUT_PROJECT_PATH, 'package.json'),
)
// removes existing Fixture and replaces with newly built project,
diff --git a/tasks/test-project/set-up-trusted-documents.ts b/tasks/test-project/set-up-trusted-documents.ts
index 5dd89f9fe41c..437d0bfc9732 100644
--- a/tasks/test-project/set-up-trusted-documents.ts
+++ b/tasks/test-project/set-up-trusted-documents.ts
@@ -24,7 +24,7 @@ async function runCommand() {
await exec(
'yarn rw setup graphql trusted-documents',
[],
- getExecaOptions(OUTPUT_PROJECT_PATH)
+ getExecaOptions(OUTPUT_PROJECT_PATH),
)
const redwoodTomlPath = path.join(OUTPUT_PROJECT_PATH, 'redwood.toml')
@@ -36,7 +36,7 @@ async function runCommand() {
if (!redwoodTomlContent.includes('trustedDocuments = true')) {
console.error(
- 'Failed to set up trusted-documents in fragments test-project'
+ 'Failed to set up trusted-documents in fragments test-project',
)
console.error('trustedDocuments = true not set in redwood.toml')
console.error()
@@ -50,14 +50,14 @@ async function runCommand() {
const graphqlHandlerPath = path.join(
OUTPUT_PROJECT_PATH,
- 'api/src/functions/graphql.ts'
+ 'api/src/functions/graphql.ts',
)
const graphqlHandlerContent = fs.readFileSync(graphqlHandlerPath, 'utf-8')
const storeImport = "import { store } from 'src/lib/trustedDocumentsStore'"
if (!graphqlHandlerContent.includes(storeImport)) {
console.error(
- 'Failed to set up trusted-documents in fragments test-project'
+ 'Failed to set up trusted-documents in fragments test-project',
)
console.error('`store` is not imported in the graphql handler')
console.error()
@@ -67,10 +67,10 @@ async function runCommand() {
if (!graphqlHandlerContent.includes('trustedDocuments: {')) {
console.error(
- 'Failed to set up trusted-documents in fragments test-project'
+ 'Failed to set up trusted-documents in fragments test-project',
)
console.error(
- 'The trustedDocuments store is not used in the graphql handler'
+ 'The trustedDocuments store is not used in the graphql handler',
)
console.error()
console.error('Please run this command locally to make sure it works')
diff --git a/tasks/test-project/tasks.js b/tasks/test-project/tasks.js
index c4fa42ef9b3e..77c995ed7529 100644
--- a/tasks/test-project/tasks.js
+++ b/tasks/test-project/tasks.js
@@ -33,7 +33,7 @@ const createBuilder = (cmd) => {
await execa(
cmd,
Array.isArray(positionals) ? positionals : [positionals],
- getExecaOptions(OUTPUT_PATH)
+ getExecaOptions(OUTPUT_PATH),
)
}
}
@@ -52,7 +52,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
return applyCodemod(
'homePage.js',
- fullPath('web/src/pages/HomePage/HomePage')
+ fullPath('web/src/pages/HomePage/HomePage'),
)
},
},
@@ -63,7 +63,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
return applyCodemod(
'aboutPage.js',
- fullPath('web/src/pages/AboutPage/AboutPage')
+ fullPath('web/src/pages/AboutPage/AboutPage'),
)
},
},
@@ -74,7 +74,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
return applyCodemod(
'contactUsPage.js',
- fullPath('web/src/pages/ContactUsPage/ContactUsPage')
+ fullPath('web/src/pages/ContactUsPage/ContactUsPage'),
)
},
},
@@ -85,7 +85,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
return applyCodemod(
'blogPostPage.js',
- fullPath('web/src/pages/BlogPostPage/BlogPostPage')
+ fullPath('web/src/pages/BlogPostPage/BlogPostPage'),
)
},
},
@@ -120,12 +120,12 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
fs.writeFileSync(
fullPath('web/src/pages/ProfilePage/ProfilePage.test'),
- testFileContent
+ testFileContent,
)
return applyCodemod(
'profilePage.js',
- fullPath('web/src/pages/ProfilePage/ProfilePage')
+ fullPath('web/src/pages/ProfilePage/ProfilePage'),
)
},
},
@@ -133,12 +133,12 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
title: 'Creating MDX Storybook stories',
task: () => {
const redwoodMdxStoryContent = fs.readFileSync(
- `${path.resolve(__dirname, 'codemods', 'Redwood.stories.mdx')}`
+ `${path.resolve(__dirname, 'codemods', 'Redwood.stories.mdx')}`,
)
fs.writeFileSync(
fullPath('web/src/Redwood.stories.mdx', { addExtension: false }),
- redwoodMdxStoryContent
+ redwoodMdxStoryContent,
)
return
@@ -151,7 +151,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
await applyCodemod(
'waterfallPage.js',
- fullPath('web/src/pages/WaterfallPage/WaterfallPage')
+ fullPath('web/src/pages/WaterfallPage/WaterfallPage'),
)
},
},
@@ -165,7 +165,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
return applyCodemod(
'blogLayout.js',
- fullPath('web/src/layouts/BlogLayout/BlogLayout')
+ fullPath('web/src/layouts/BlogLayout/BlogLayout'),
)
}
@@ -176,24 +176,24 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
await applyCodemod(
'blogPost.js',
- fullPath('web/src/components/BlogPost/BlogPost')
+ fullPath('web/src/components/BlogPost/BlogPost'),
)
await createComponent('author')
await applyCodemod(
'author.js',
- fullPath('web/src/components/Author/Author')
+ fullPath('web/src/components/Author/Author'),
)
await applyCodemod(
'updateAuthorStories.js',
- fullPath('web/src/components/Author/Author.stories')
+ fullPath('web/src/components/Author/Author.stories'),
)
await applyCodemod(
'updateAuthorTest.js',
- fullPath('web/src/components/Author/Author.test')
+ fullPath('web/src/components/Author/Author.test'),
)
}
@@ -204,28 +204,30 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
await applyCodemod(
'blogPostsCell.js',
- fullPath('web/src/components/BlogPostsCell/BlogPostsCell')
+ fullPath('web/src/components/BlogPostsCell/BlogPostsCell'),
)
await createCell('blogPost')
await applyCodemod(
'blogPostCell.js',
- fullPath('web/src/components/BlogPostCell/BlogPostCell')
+ fullPath('web/src/components/BlogPostCell/BlogPostCell'),
)
await createCell('author')
await applyCodemod(
'authorCell.js',
- fullPath('web/src/components/AuthorCell/AuthorCell')
+ fullPath('web/src/components/AuthorCell/AuthorCell'),
)
await createCell('waterfallBlogPost')
return applyCodemod(
'waterfallBlogPostCell.js',
- fullPath('web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell')
+ fullPath(
+ 'web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell',
+ ),
)
}
@@ -234,21 +236,21 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
'updateBlogPostMocks.js',
fullPath('web/src/components/BlogPostCell/BlogPostCell.mock.ts', {
addExtension: false,
- })
+ }),
)
await applyCodemod(
'updateBlogPostMocks.js',
fullPath('web/src/components/BlogPostsCell/BlogPostsCell.mock.ts', {
addExtension: false,
- })
+ }),
)
await applyCodemod(
'updateAuthorCellMock.js',
fullPath('web/src/components/AuthorCell/AuthorCell.mock.ts', {
addExtension: false,
- })
+ }),
)
return applyCodemod(
@@ -257,8 +259,8 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
'web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.mock.ts',
{
addExtension: false,
- }
- )
+ },
+ ),
)
}
@@ -298,7 +300,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
execa(
'yarn workspace web add -D postcss postcss-loader tailwindcss autoprefixer prettier-plugin-tailwindcss@^0.5.12',
[],
- getExecaOptions(outputPath)
+ getExecaOptions(outputPath),
),
enabled: () => linkWithLatestFwBuild,
},
@@ -316,9 +318,9 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
return execa(
'yarn rw setup ui tailwindcss',
['--force', linkWithLatestFwBuild && '--no-install'].filter(
- Boolean
+ Boolean,
),
- getExecaOptions(outputPath)
+ getExecaOptions(outputPath),
)
},
},
@@ -326,7 +328,7 @@ async function webTasks(outputPath, { linkWithLatestFwBuild, verbose }) {
{
exitOnError: true,
renderer: verbose && 'verbose',
- }
+ },
)
}
@@ -354,7 +356,7 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
outputPath,
'node_modules',
'@redwoodjs',
- 'auth-dbauth-setup'
+ 'auth-dbauth-setup',
)
// At an earlier step we run `yarn rwfw project:copy` which gives us
@@ -368,7 +370,7 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
await execa(
'yarn rw setup auth dbAuth --force --no-webauthn',
[],
- getExecaOptions(outputPath)
+ getExecaOptions(outputPath),
)
// Restore postinstall script
@@ -386,7 +388,7 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
await execa(
'yarn rw g dbAuth --no-webauthn --username-label=username --password-label=password',
[],
- execaOptions
+ execaOptions,
)
// update directive in contacts.sdl.ts
@@ -395,11 +397,11 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
const resultsContactsSdl = contentContactsSdl
.replace(
'createContact(input: CreateContactInput!): Contact! @requireAuth',
- `createContact(input: CreateContactInput!): Contact @skipAuth`
+ `createContact(input: CreateContactInput!): Contact @skipAuth`,
)
.replace(
'deleteContact(id: Int!): Contact! @requireAuth',
- 'deleteContact(id: Int!): Contact! @requireAuth(roles:["ADMIN"])'
+ 'deleteContact(id: Int!): Contact! @requireAuth(roles:["ADMIN"])',
) // make deleting contacts admin only
fs.writeFileSync(pathContactsSdl, resultsContactsSdl)
@@ -409,7 +411,7 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
const resultsPostsSdl = contentPostsSdl.replace(
/posts: \[Post!\]! @requireAuth([^}]*)@requireAuth/,
`posts: [Post!]! @skipAuth
- post(id: Int!): Post @skipAuth`
+ post(id: Int!): Post @skipAuth`,
) // make posts accessible to all
fs.writeFileSync(pathPostsSdl, resultsPostsSdl)
@@ -421,11 +423,11 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
const newLibAuthContent = libAuthContent
.replace(
'select: { id: true }',
- 'select: { id: true, roles: true, email: true}'
+ 'select: { id: true, roles: true, email: true}',
)
.replace(
'const currentUserRoles = context.currentUser?.roles',
- 'const currentUserRoles = context.currentUser?.roles as string | string[]'
+ 'const currentUserRoles = context.currentUser?.roles as string | string[]',
)
fs.writeFileSync(libAuthPath, newLibAuthContent)
@@ -436,7 +438,7 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
/const mockExecution([^}]*){} }\)/,
`const mockExecution = mockRedwoodDirective(requireAuth, {
context: { currentUser: { id: 1, roles: 'ADMIN', email: 'b@zinga.com' } },
- })`
+ })`,
)
fs.writeFileSync(pathRequireAuth, resultsRequireAuth)
@@ -444,7 +446,7 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
const pathSignupPageTs = `${OUTPUT_PATH}/web/src/pages/SignupPage/SignupPage.tsx`
const contentSignupPageTs = fs.readFileSync(pathSignupPageTs, 'utf-8')
const usernameFields = contentSignupPageTs.match(
- /\s*/
+ /\s*/,
)[0]
const fullNameFields = usernameFields
.replace(/\s*ref=\{usernameRef}/, '')
@@ -455,12 +457,12 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
.replace(
'',
'\n' +
- fullNameFields
+ fullNameFields,
)
// include full-name in the data we pass to `signUp()`
.replace(
'password: data.password',
- "password: data.password, 'full-name': data['full-name']"
+ "password: data.password, 'full-name': data['full-name']",
)
fs.writeFileSync(pathSignupPageTs, newContentSignupPageTs)
@@ -473,7 +475,7 @@ async function apiTasks(outputPath, { verbose, linkWithLatestFwBuild }) {
.replace('userAttributes: _userAttributes', 'userAttributes')
.replace(
'// name: userAttributes.name',
- "fullName: userAttributes['full-name']"
+ "fullName: userAttributes['full-name']",
)
fs.writeFileSync(pathAuthTs, resultsAuthTs)
@@ -518,7 +520,7 @@ export default DoublePage`
fs.writeFileSync(
fullPath('web/src/pages/DoublePage/DoublePage'),
- doublePageContent
+ doublePageContent,
)
},
},
@@ -529,31 +531,31 @@ export default DoublePage`
const contentRoutes = fs.readFileSync(pathRoutes).toString()
const resultsRoutesAbout = contentRoutes.replace(
/name="about"/,
- `name="about" prerender`
+ `name="about" prerender`,
)
const resultsRoutesHome = resultsRoutesAbout.replace(
/name="home"/,
- `name="home" prerender`
+ `name="home" prerender`,
)
const resultsRoutesBlogPost = resultsRoutesHome.replace(
/name="blogPost"/,
- `name="blogPost" prerender`
+ `name="blogPost" prerender`,
)
const resultsRoutesNotFound = resultsRoutesBlogPost.replace(
/page={NotFoundPage}/,
- `page={NotFoundPage} prerender`
+ `page={NotFoundPage} prerender`,
)
const resultsRoutesWaterfall = resultsRoutesNotFound.replace(
/page={WaterfallPage}/,
- `page={WaterfallPage} prerender`
+ `page={WaterfallPage} prerender`,
)
const resultsRoutesDouble = resultsRoutesWaterfall.replace(
'name="double"',
- 'name="double" prerender'
+ 'name="double" prerender',
)
const resultsRoutesNewContact = resultsRoutesDouble.replace(
'name="newContact"',
- 'name="newContact" prerender'
+ 'name="newContact" prerender',
)
fs.writeFileSync(pathRoutes, resultsRoutesNewContact)
@@ -593,7 +595,7 @@ export default DoublePage`
return execa(
`yarn rw prisma migrate dev --name create_post_user`,
[],
- getExecaOptions(outputPath)
+ getExecaOptions(outputPath),
)
},
},
@@ -605,7 +607,7 @@ export default DoublePage`
// Replace the random numbers in the scenario with consistent values
await applyCodemod(
'scenarioValueSuffix.js',
- fullPath('api/src/services/posts/posts.scenarios')
+ fullPath('api/src/services/posts/posts.scenarios'),
)
await execa(`yarn rwfw project:copy`, [], getExecaOptions(outputPath))
@@ -616,7 +618,7 @@ export default DoublePage`
task: async () => {
await applyCodemod(
'seed.js',
- fullPath('scripts/seed.ts', { addExtension: false })
+ fullPath('scripts/seed.ts', { addExtension: false }),
)
},
},
@@ -630,7 +632,7 @@ export default DoublePage`
await execa(
`yarn rw prisma migrate dev --name create_contact`,
[],
- getExecaOptions(outputPath)
+ getExecaOptions(outputPath),
)
await generateScaffold('contacts')
@@ -644,7 +646,7 @@ export default DoublePage`
OUTPUT_PATH,
'api',
'db',
- 'migrations'
+ 'migrations',
)
// Migration folders are folders which start with 14 digits because they have a yyyymmddhhmmss
const migrationFolders = fs
@@ -671,8 +673,8 @@ export default DoublePage`
path.join(migrationsFolderPath, name),
path.join(
migrationsFolderPath,
- `${datetimeInCorrectFormat}${name.substring(14)}`
- )
+ `${datetimeInCorrectFormat}${name.substring(14)}`,
+ ),
)
datetime.setDate(datetime.getDate() + 1)
})
@@ -691,18 +693,18 @@ export default DoublePage`
await applyCodemod(
'usersSdl.js',
- fullPath('api/src/graphql/users.sdl')
+ fullPath('api/src/graphql/users.sdl'),
)
await applyCodemod(
'usersService.js',
- fullPath('api/src/services/users/users')
+ fullPath('api/src/services/users/users'),
)
// Replace the random numbers in the scenario with consistent values
await applyCodemod(
'scenarioValueSuffix.js',
- fullPath('api/src/services/users/users.scenarios')
+ fullPath('api/src/services/users/users.scenarios'),
)
const test = `import { user } from './users'
@@ -727,7 +729,7 @@ export default DoublePage`
// Copy contact.scenarios.ts, because scenario tests look for the same filename
fs.copyFileSync(
fullPath('api/src/services/contacts/contacts.scenarios'),
- fullPath('api/src/services/contacts/describeContacts.scenarios')
+ fullPath('api/src/services/contacts/describeContacts.scenarios'),
)
// Create describeContacts.test.ts
@@ -735,12 +737,12 @@ export default DoublePage`
__dirname,
'templates',
'api',
- 'contacts.describeScenario.test.ts.template'
+ 'contacts.describeScenario.test.ts.template',
)
fs.copyFileSync(
describeScenarioFixture,
- fullPath('api/src/services/contacts/describeContacts.test')
+ fullPath('api/src/services/contacts/describeContacts.test'),
)
},
},
@@ -757,7 +759,7 @@ export default DoublePage`
exitOnError: true,
renderer: verbose && 'verbose',
renderOptions: { collapseSubtasks: false },
- }
+ },
)
}
@@ -777,7 +779,7 @@ async function streamingTasks(outputPath, { verbose }) {
await applyCodemod(
'delayedPage.js',
- fullPath('web/src/pages/DelayedPage/DelayedPage')
+ fullPath('web/src/pages/DelayedPage/DelayedPage'),
)
},
},
@@ -785,7 +787,7 @@ async function streamingTasks(outputPath, { verbose }) {
title: 'Enable streaming-ssr experiment',
task: async () => {
const setupExperiment = createBuilder(
- 'yarn rw experimental setup-streaming-ssr'
+ 'yarn rw experimental setup-streaming-ssr',
)
await setupExperiment('--force')
},
@@ -828,7 +830,7 @@ async function fragmentsTasks(outputPath, { verbose }) {
return exec(
'yarn rw prisma migrate dev --name create_produce_stall',
[],
- getExecaOptions(outputPath)
+ getExecaOptions(outputPath),
)
},
},
@@ -837,7 +839,7 @@ async function fragmentsTasks(outputPath, { verbose }) {
task: async () => {
await applyCodemod(
'seedFragments.ts',
- fullPath('scripts/seed.ts', { addExtension: false })
+ fullPath('scripts/seed.ts', { addExtension: false }),
)
await exec('yarn rw prisma db seed', [], getExecaOptions(outputPath))
@@ -853,7 +855,7 @@ async function fragmentsTasks(outputPath, { verbose }) {
await applyCodemod(
'producesSdl.ts',
- fullPath('api/src/graphql/produces.sdl')
+ fullPath('api/src/graphql/produces.sdl'),
)
},
},
@@ -865,7 +867,7 @@ async function fragmentsTasks(outputPath, { verbose }) {
OUTPUT_PATH,
'web',
'src',
- 'components'
+ 'components',
)
for (const fileName of [
@@ -905,7 +907,7 @@ async function fragmentsTasks(outputPath, { verbose }) {
await applyCodemod(
'groceriesPage.ts',
- fullPath('web/src/pages/GroceriesPage/GroceriesPage')
+ fullPath('web/src/pages/GroceriesPage/GroceriesPage'),
)
},
},
diff --git a/tasks/test-project/templates/web/FruitInfo.tsx b/tasks/test-project/templates/web/FruitInfo.tsx
index 95015ee57764..67a03889a5cc 100644
--- a/tasks/test-project/templates/web/FruitInfo.tsx
+++ b/tasks/test-project/templates/web/FruitInfo.tsx
@@ -5,19 +5,17 @@ import { registerFragment } from '@redwoodjs/web/apollo'
import Card from 'src/components/Card'
import StallInfo from 'src/components/StallInfo'
-const { useRegisteredFragment } = registerFragment(
- gql`
- fragment Fruit_info on Fruit {
- id
- name
- isSeedless
- ripenessIndicators
- stall {
- ...Stall_info
- }
+const { useRegisteredFragment } = registerFragment(gql`
+ fragment Fruit_info on Fruit {
+ id
+ name
+ isSeedless
+ ripenessIndicators
+ stall {
+ ...Stall_info
}
- `
-)
+ }
+`)
const FruitInfo = ({ id }: { id: string }) => {
const { data: fruit, complete } = useRegisteredFragment(id)
diff --git a/tasks/test-project/templates/web/ProduceInfo.tsx b/tasks/test-project/templates/web/ProduceInfo.tsx
index f06a68ad5e9d..39e06a874703 100644
--- a/tasks/test-project/templates/web/ProduceInfo.tsx
+++ b/tasks/test-project/templates/web/ProduceInfo.tsx
@@ -4,14 +4,12 @@ import { registerFragment } from '@redwoodjs/web/apollo'
import Card from 'src/components/Card'
-const { useRegisteredFragment } = registerFragment(
- gql`
- fragment Produce_info on Produce {
- id
- name
- }
- `
-)
+const { useRegisteredFragment } = registerFragment(gql`
+ fragment Produce_info on Produce {
+ id
+ name
+ }
+`)
const ProduceInfo = ({ id }: { id: string }) => {
const { data, complete } = useRegisteredFragment(id)
diff --git a/tasks/test-project/templates/web/StallInfo.tsx b/tasks/test-project/templates/web/StallInfo.tsx
index 24b2fbb58d35..4178b6ea93af 100644
--- a/tasks/test-project/templates/web/StallInfo.tsx
+++ b/tasks/test-project/templates/web/StallInfo.tsx
@@ -2,14 +2,12 @@ import type { Stall } from 'types/graphql'
import { registerFragment } from '@redwoodjs/web/apollo'
-const { useRegisteredFragment } = registerFragment(
- gql`
- fragment Stall_info on Stall {
- id
- name
- }
- `
-)
+const { useRegisteredFragment } = registerFragment(gql`
+ fragment Stall_info on Stall {
+ id
+ name
+ }
+`)
const StallInfo = ({ id }: { id: string }) => {
const { data, complete } = useRegisteredFragment(id)
diff --git a/tasks/test-project/templates/web/VegetableInfo.tsx b/tasks/test-project/templates/web/VegetableInfo.tsx
index 96f6208b19e9..6954d3fe5f5b 100644
--- a/tasks/test-project/templates/web/VegetableInfo.tsx
+++ b/tasks/test-project/templates/web/VegetableInfo.tsx
@@ -5,19 +5,17 @@ import { registerFragment } from '@redwoodjs/web/apollo'
import Card from 'src/components/Card'
import StallInfo from 'src/components/StallInfo'
-const { useRegisteredFragment } = registerFragment(
- gql`
- fragment Vegetable_info on Vegetable {
- id
- name
- vegetableFamily
- isPickled
- stall {
- ...Stall_info
- }
+const { useRegisteredFragment } = registerFragment(gql`
+ fragment Vegetable_info on Vegetable {
+ id
+ name
+ vegetableFamily
+ isPickled
+ stall {
+ ...Stall_info
}
- `
-)
+ }
+`)
const VegetableInfo = ({ id }: { id: string }) => {
const { data: vegetable, complete } = useRegisteredFragment(id)
diff --git a/tasks/test-project/util.js b/tasks/test-project/util.js
index a4dd2c105a8e..78d577c25c3e 100644
--- a/tasks/test-project/util.js
+++ b/tasks/test-project/util.js
@@ -36,7 +36,7 @@ const getExecaOptions = (cwd) => ({
const updatePkgJsonScripts = ({ projectPath, scripts }) => {
const projectPackageJsonPath = path.join(projectPath, 'package.json')
const projectPackageJson = JSON.parse(
- fs.readFileSync(projectPackageJsonPath, 'utf-8')
+ fs.readFileSync(projectPackageJsonPath, 'utf-8'),
)
projectPackageJson.scripts = {
...projectPackageJson.scripts,
@@ -44,7 +44,7 @@ const updatePkgJsonScripts = ({ projectPath, scripts }) => {
}
fs.writeFileSync(
projectPackageJsonPath,
- JSON.stringify(projectPackageJson, undefined, 2)
+ JSON.stringify(projectPackageJson, undefined, 2),
)
}
@@ -66,7 +66,7 @@ async function confirmNoFixtureNoLink(copyFromFixtureOption, linkOption) {
onCancel: () => {
process.exit(1)
},
- }
+ },
)
return checkNoLink
}
diff --git a/tasks/tsconfig.json b/tasks/tsconfig.json
index dcfb6070d4d2..8598869e8c39 100644
--- a/tasks/tsconfig.json
+++ b/tasks/tsconfig.json
@@ -2,6 +2,6 @@
"extends": "../tsconfig.compilerOption.json",
"compilerOptions": {
"moduleResolution": "NodeNext",
- "module": "NodeNext",
- },
+ "module": "NodeNext"
+ }
}
diff --git a/tasks/update-package-versions b/tasks/update-package-versions
index 3ea10c4f183c..e7a2beaa650c 100755
--- a/tasks/update-package-versions
+++ b/tasks/update-package-versions
@@ -11,7 +11,7 @@ async function run() {
if (!version) {
console.error(
- 'You have to provide a version.\nUsage ./update-package-versions '
+ 'You have to provide a version.\nUsage ./update-package-versions ',
)
process.exitCode = 1
return
@@ -40,7 +40,7 @@ async function run() {
console.log('Updating CRWA template...')
const tsTemplatePath = path.join(
cwd,
- 'packages/create-redwood-app/templates/ts'
+ 'packages/create-redwood-app/templates/ts',
)
updateRWJSPkgsVersion(tsTemplatePath, version)
updateRWJSPkgsVersion(path.join(tsTemplatePath, 'api'), version)
@@ -49,7 +49,7 @@ async function run() {
const jsTemplatePath = path.join(
cwd,
- 'packages/create-redwood-app/templates/js'
+ 'packages/create-redwood-app/templates/js',
)
updateRWJSPkgsVersion(jsTemplatePath, version)
updateRWJSPkgsVersion(path.join(jsTemplatePath, 'api'), version)
From cb6a323f901aa0883e785b92f363932c1a5645ae Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Fri, 16 Aug 2024 05:51:41 +0100
Subject: [PATCH 32/52] chore(yarn): update to use js config for constraints
(#11290)
We currently use a prolog file to define the constraints yarn applies
when you run `yarn constraints` (which is run with `yarn check`).
You can see [here](https://yarnpkg.com/features/constraints) that yarn
states this format should be considered deprecated. Instead a
`yarn.config.cjs` file is recommended. This change switches our current
prolog one to this new js one.
I added rules to enforce some consistent and correctly specified fields
are present on our package.json files. We can add to this in the future
should we wish to.
---
.github/workflows/ci.yml | 2 +-
constraints.pro | 27 ------
package.json | 1 +
yarn.config.cjs | 181 +++++++++++++++++++++++++++++++++++++++
yarn.lock | 10 +++
5 files changed, 193 insertions(+), 28 deletions(-)
delete mode 100644 constraints.pro
create mode 100644 yarn.config.cjs
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9c8b5111a84e..c66b3505a0ea 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -52,7 +52,7 @@ jobs:
uses: ./.github/actions/set-up-job
with:
set-up-yarn-cache: false
- yarn-install-directory: ./tasks/check
+ yarn-install-directory: .
build: false
- name: ✅ Check constraints, dependencies, and package.json's
diff --git a/constraints.pro b/constraints.pro
deleted file mode 100644
index 463749739678..000000000000
--- a/constraints.pro
+++ /dev/null
@@ -1,27 +0,0 @@
-% Yarn Constraints https://yarnpkg.com/features/constraints
-% check with "yarn constraints" (fix w/ "yarn constraints --fix")
-% reference for other constraints: https://github.com/babel/babel/blob/main/constraints.pro
-
-% Enforces that a dependency doesn't appear in both `dependencies` and `devDependencies`
-gen_enforced_dependency(WorkspaceCwd, DependencyIdent, null, 'devDependencies') :-
- workspace_has_dependency(WorkspaceCwd, DependencyIdent, _, 'devDependencies'),
- workspace_has_dependency(WorkspaceCwd, DependencyIdent, _, 'dependencies').
-
-% Prevent two workspaces from depending on conflicting versions of a same dependency
-gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange2, DependencyType) :-
- workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType),
- workspace_has_dependency(OtherWorkspaceCwd, DependencyIdent, DependencyRange2, DependencyType2),
- DependencyRange \= DependencyRange2.
-
-% Enforce that all workspaces building with Babel depend on '@babel/runtime-corejs3' and 'core-js'.
-gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, 'dependencies') :-
- member(DependencyIdent, [
- '@babel/runtime-corejs3',
- 'core-js'
- ]),
- % Exclude the root workspace
- WorkspaceCwd \= '.',
- % Only target workspaces with a build:js script
- workspace_field(WorkspaceCwd, 'scripts.build:js', _),
- % Get the range from the root workspace
- workspace_has_dependency('.', DependencyIdent, DependencyRange, _).
diff --git a/package.json b/package.json
index 2fac09ffa27c..b92811a7c404 100644
--- a/package.json
+++ b/package.json
@@ -87,6 +87,7 @@
"@types/prompts": "2.4.9",
"@typescript-eslint/eslint-plugin": "8.1.0",
"@typescript-eslint/parser": "8.1.0",
+ "@yarnpkg/types": "4.0.0",
"all-contributors-cli": "6.26.1",
"babel-jest": "^29.7.0",
"babel-plugin-auto-import": "1.1.0",
diff --git a/yarn.config.cjs b/yarn.config.cjs
new file mode 100644
index 000000000000..7b550da914bc
--- /dev/null
+++ b/yarn.config.cjs
@@ -0,0 +1,181 @@
+/* eslint-env node */
+// @ts-check
+
+/**
+ * @typedef {import('@yarnpkg/types').Yarn.Constraints.Context} Context
+ * @typedef {import('@yarnpkg/types').Yarn.Constraints.Workspace} Workspace
+ */
+
+/** @type {import('@yarnpkg/types')} */
+const { defineConfig } = require(`@yarnpkg/types`)
+
+/**
+ * This rule will enforce that a workspace MUST depend on the same version of a
+ * dependency as the one used by the other workspaces.
+ *
+ * @param {Context} context
+ */
+function enforceConsistentDependenciesAcrossTheProject({ Yarn }) {
+ for (const dependency of Yarn.dependencies()) {
+ if (dependency.type === `peerDependencies`) {
+ continue
+ }
+
+ for (const otherDependency of Yarn.dependencies({
+ ident: dependency.ident,
+ })) {
+ if (otherDependency.type === `peerDependencies`) {
+ continue
+ }
+
+ if (
+ (dependency.type === `devDependencies` ||
+ otherDependency.type === `devDependencies`) &&
+ Yarn.workspace({ ident: otherDependency.ident })
+ ) {
+ continue
+ }
+
+ dependency.update(otherDependency.range)
+ }
+ }
+}
+
+/**
+ * This rule will enforce that a workspace MUST depend on the same version of a
+ * dependency as the one used by the other workspaces.
+ *
+ * @param {Context} context
+ */
+function enforceWorkspaceDependenciesWhenPossible({ Yarn }) {
+ for (const dependency of Yarn.dependencies()) {
+ if (!Yarn.workspace({ ident: dependency.ident })) {
+ continue
+ }
+
+ dependency.update(`workspace:*`)
+ }
+}
+
+/**
+ * This rule will enforce that a dependency doesn't appear in both `dependencies`
+ * and `devDependencies`.
+ *
+ * @param {Context} context
+ */
+function enforceNotProdAndDevDependencies({ Yarn }) {
+ for (const workspace of Yarn.workspaces()) {
+ const dependencies = Yarn.dependencies({ workspace, type: 'dependencies' })
+ const devDependencies = Yarn.dependencies({
+ workspace,
+ type: 'devDependencies',
+ })
+ for (const dependency of dependencies) {
+ if (
+ devDependencies.find(
+ (devDependency) => devDependency.ident === dependency.ident,
+ )
+ ) {
+ dependency.error(
+ `The dependency '${dependency.ident}' should not appear in both dependencies and devDependencies`,
+ )
+ }
+ }
+ }
+}
+
+/**
+ * This rule will enforce that any package built with babel (identified by the
+ * presence of a 'build:js' script in its `package.json`) must depend on the
+ * '@babel/runtime-corejs3' and 'core-js' packages.
+ *
+ * @param {Context} context
+ */
+function enforceBabelDependencies({ Yarn }) {
+ for (const workspace of Yarn.workspaces()) {
+ const packageJson = workspace.manifest
+ if (!packageJson.scripts?.[`build:js`]) {
+ continue
+ }
+
+ const dependencies = Yarn.dependencies({
+ workspace,
+ type: 'dependencies',
+ })
+ const requiredDependencies = [`@babel/runtime-corejs3`, `core-js`]
+ for (const dependency of requiredDependencies) {
+ if (!dependencies.find((dep) => dep.ident === dependency)) {
+ workspace.error(
+ `The package '${workspace.cwd}' must depend on '${dependency}' to build with babel`,
+ )
+ }
+ }
+ }
+}
+
+/**
+ * This rule will enforce that the specified fields are present in the
+ * `package.json` of all workspaces.
+ *
+ * @param {Context} context
+ * @param {string[]} fields
+ */
+function enforceFieldsOnAllWorkspaces({ Yarn }, fields) {
+ for (const workspace of Yarn.workspaces()) {
+ // Skip the root workspace
+ if (workspace.cwd === '.') {
+ continue
+ }
+
+ for (const field of fields) {
+ if (!workspace.manifest[field]) {
+ workspace.error(
+ `The field '${field}' is required in the package.json of '${workspace.cwd}'`,
+ )
+ }
+ }
+ }
+}
+
+/**
+ * This rule will enforce that the specified fields are present in the
+ * `package.json` of all workspaces and that they have the expected value.
+ *
+ * @param {Context} context
+ * @param {Record any) | string>} fields
+ */
+function enforceFieldsWithValuesOnAllWorkspaces({ Yarn }, fields) {
+ for (const workspace of Yarn.workspaces()) {
+ // Skip the root workspace
+ if (workspace.cwd === '.') {
+ continue
+ }
+
+ for (const [field, value] of Object.entries(fields)) {
+ workspace.set(
+ field,
+ typeof value === `function` ? value(workspace) : value,
+ )
+ }
+ }
+}
+
+module.exports = defineConfig({
+ constraints: async (ctx) => {
+ enforceConsistentDependenciesAcrossTheProject(ctx)
+ enforceWorkspaceDependenciesWhenPossible(ctx)
+ enforceNotProdAndDevDependencies(ctx)
+ enforceBabelDependencies(ctx)
+ enforceFieldsOnAllWorkspaces(ctx, [
+ 'name',
+ 'version',
+ // 'description', // TODO(jgmw): Add description to all packages and uncomment this line
+ ])
+ enforceFieldsWithValuesOnAllWorkspaces(ctx, {
+ license: 'MIT',
+ ['repository.type']: 'git',
+ ['repository.url']: 'git+https://github.com/redwoodjs/redwood.git',
+ ['repository.directory']: (workspace) => workspace.cwd,
+ })
+ },
+})
diff --git a/yarn.lock b/yarn.lock
index dad43c6f8b77..a77c81799160 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11648,6 +11648,15 @@ __metadata:
languageName: node
linkType: hard
+"@yarnpkg/types@npm:4.0.0":
+ version: 4.0.0
+ resolution: "@yarnpkg/types@npm:4.0.0"
+ dependencies:
+ tslib: "npm:^2.4.0"
+ checksum: 10c0/41f67a4aa5c414c1e228f51453451fa15e0dd70c5cf2b1ae1ca142a3f018f25e4a37e60372cd0f5970c755e1804a2e31e208bff427add1cf13f899b0b9adc1e0
+ languageName: node
+ linkType: hard
+
"@zkochan/js-yaml@npm:0.0.7":
version: 0.0.7
resolution: "@zkochan/js-yaml@npm:0.0.7"
@@ -26656,6 +26665,7 @@ __metadata:
"@types/prompts": "npm:2.4.9"
"@typescript-eslint/eslint-plugin": "npm:8.1.0"
"@typescript-eslint/parser": "npm:8.1.0"
+ "@yarnpkg/types": "npm:4.0.0"
all-contributors-cli: "npm:6.26.1"
babel-jest: "npm:^29.7.0"
babel-plugin-auto-import: "npm:1.1.0"
From 49ddfd4047b9081489c3394020877c5c0b3b7496 Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Fri, 16 Aug 2024 17:12:41 +0100
Subject: [PATCH 33/52] chore: delete crowdin config file (#11291)
I don't really have any context around this file. I wasn't with the
project when it was last touched. It hasn't been updated in years and
the content inside it looks outdated.
I would suggest we just delete file and if we need to look into our
translation setup again then we can do so. No point in an old file
laying around if it's not providing value.
---
crowdin.yml | 5 -----
1 file changed, 5 deletions(-)
delete mode 100644 crowdin.yml
diff --git a/crowdin.yml b/crowdin.yml
deleted file mode 100644
index 6cbb752d4ee3..000000000000
--- a/crowdin.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-files:
- - source: /learn.redwoodjs.com/docs/tutorial
- translation: /learn.redwoodjs.com/i18n/%two_letters_code%/tutorial/%original_file_name%
- - source: /learn.redwoodjs.com/docs/tutorial2
- translation: /learn.redwoodjs.com/i18n/%two_letters_code%/tutorial2/%original_file_name%
From 1f328ef4820a7554f81b88ebd680459b4d2327ae Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Fri, 16 Aug 2024 17:53:27 +0100
Subject: [PATCH 34/52] chore(check): Refactor 'yarn check' away from being a
standalone node script (#11292)
The `yarn check` script was implemented as a standalone node program
that executed a couple of other yarn commands in series. To simplify we
can just run the two commands in the shell and avoid maintaining a whole
node program just to do the same thing.
---
.github/workflows/ci.yml | 2 +-
package.json | 2 +-
tasks/check/action.yml | 5 -
tasks/check/check.mjs | 29 ----
tasks/check/package.json | 10 --
tasks/check/yarn.lock | 352 ---------------------------------------
6 files changed, 2 insertions(+), 398 deletions(-)
delete mode 100644 tasks/check/action.yml
delete mode 100644 tasks/check/check.mjs
delete mode 100644 tasks/check/package.json
delete mode 100644 tasks/check/yarn.lock
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c66b3505a0ea..266936d63bae 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -56,7 +56,7 @@ jobs:
build: false
- name: ✅ Check constraints, dependencies, and package.json's
- uses: ./tasks/check
+ run: yarn check
check-skip:
needs: detect-changes
diff --git a/package.json b/package.json
index b92811a7c404..17f654b6d17e 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
"build:test-project": "node ./tasks/test-project/test-project",
"build:watch": "lerna run build:watch --parallel; tsc --build",
"changesets": "tsx ./tasks/changesets/changesets.mts",
- "check": "node ./tasks/check/check.mjs",
+ "check": "cross-env yarn constraints && yarn dedupe --check",
"check:package": "nx run-many -t check:package --output-style static",
"clean:prisma": "rimraf node_modules/.prisma/client && node node_modules/@prisma/client/scripts/postinstall.js",
"e2e": "node ./tasks/run-e2e",
diff --git a/tasks/check/action.yml b/tasks/check/action.yml
deleted file mode 100644
index 913458202a20..000000000000
--- a/tasks/check/action.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: 'Check'
-description: "Check constraints, dependencies, and package.json's"
-runs:
- using: node20
- main: 'check.mjs'
diff --git a/tasks/check/check.mjs b/tasks/check/check.mjs
deleted file mode 100644
index edf76826bb40..000000000000
--- a/tasks/check/check.mjs
+++ /dev/null
@@ -1,29 +0,0 @@
-/* eslint-env es6, node */
-import core from '@actions/core'
-import { exec } from '@actions/exec'
-
-const checks = [
- {
- command: 'yarn constraints',
- fix: 'You can fix this by running "yarn constraints --fix"',
- },
- {
- command: 'yarn dedupe --check',
- fix: 'You can fix this by running "yarn dedupe"',
- },
- // {
- // command:
- // 'yarn workspaces foreach --all --parallel run sort-package-json --check',
- // fix: 'You can fix this by running "yarn workspaces foreach --parallel dlx sort-package-json"',
- // },
-]
-
-for (const { command, fix } of checks) {
- try {
- await exec(command)
- console.log(`"${command}" passed`)
- } catch (_e) {
- core.setFailed(`"${command}" failed`)
- console.log(fix)
- }
-}
diff --git a/tasks/check/package.json b/tasks/check/package.json
deleted file mode 100644
index 16948c9c9ec8..000000000000
--- a/tasks/check/package.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "check",
- "private": true,
- "dependencies": {
- "@actions/core": "1.10.1",
- "@actions/exec": "1.1.1",
- "sort-package-json": "2.10.0"
- },
- "packageManager": "yarn@4.4.0"
-}
diff --git a/tasks/check/yarn.lock b/tasks/check/yarn.lock
deleted file mode 100644
index a7af630cb5d9..000000000000
--- a/tasks/check/yarn.lock
+++ /dev/null
@@ -1,352 +0,0 @@
-# This file is generated by running "yarn install" inside your project.
-# Manual changes might be lost - proceed with caution!
-
-__metadata:
- version: 8
- cacheKey: 10c0
-
-"@actions/core@npm:1.10.1":
- version: 1.10.1
- resolution: "@actions/core@npm:1.10.1"
- dependencies:
- "@actions/http-client": "npm:^2.0.1"
- uuid: "npm:^8.3.2"
- checksum: 10c0/7a61446697a23dcad3545cf0634dedbdedf20ae9a0ee6ee977554589a15deb4a93593ee48a41258933d58ce0778f446b0d2c162b60750956fb75e0b9560fb832
- languageName: node
- linkType: hard
-
-"@actions/exec@npm:1.1.1":
- version: 1.1.1
- resolution: "@actions/exec@npm:1.1.1"
- dependencies:
- "@actions/io": "npm:^1.0.1"
- checksum: 10c0/4a09f6bdbe50ce68b5cf8a7254d176230d6a74bccf6ecc3857feee209a8c950ba9adec87cc5ecceb04110182d1c17117234e45557d72fde6229b7fd3a395322a
- languageName: node
- linkType: hard
-
-"@actions/http-client@npm:^2.0.1":
- version: 2.0.1
- resolution: "@actions/http-client@npm:2.0.1"
- dependencies:
- tunnel: "npm:^0.0.6"
- checksum: 10c0/b58987ba2f53d7988f612ede7ff834573a3360c21f8fdea9fea92f26ada0fd0efafb22aa7d83f49c18965a5b765775d5253e2edb8d9476d924c4b304ef726b67
- languageName: node
- linkType: hard
-
-"@actions/io@npm:^1.0.1":
- version: 1.1.2
- resolution: "@actions/io@npm:1.1.2"
- checksum: 10c0/61c871bbee1cf58f57917d9bb2cf6bb7ea4dc40de3f65c7fb4ec619ceff57fc98f56be9cca2d476b09e7a96e1cba0d88cd125c4f690d384b9483935186f256c1
- languageName: node
- linkType: hard
-
-"@nodelib/fs.scandir@npm:2.1.5":
- version: 2.1.5
- resolution: "@nodelib/fs.scandir@npm:2.1.5"
- dependencies:
- "@nodelib/fs.stat": "npm:2.0.5"
- run-parallel: "npm:^1.1.9"
- checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb
- languageName: node
- linkType: hard
-
-"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2":
- version: 2.0.5
- resolution: "@nodelib/fs.stat@npm:2.0.5"
- checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d
- languageName: node
- linkType: hard
-
-"@nodelib/fs.walk@npm:^1.2.3":
- version: 1.2.8
- resolution: "@nodelib/fs.walk@npm:1.2.8"
- dependencies:
- "@nodelib/fs.scandir": "npm:2.1.5"
- fastq: "npm:^1.6.0"
- checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1
- languageName: node
- linkType: hard
-
-"braces@npm:^3.0.2":
- version: 3.0.3
- resolution: "braces@npm:3.0.3"
- dependencies:
- fill-range: "npm:^7.1.1"
- checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04
- languageName: node
- linkType: hard
-
-"check@workspace:.":
- version: 0.0.0-use.local
- resolution: "check@workspace:."
- dependencies:
- "@actions/core": "npm:1.10.1"
- "@actions/exec": "npm:1.1.1"
- sort-package-json: "npm:2.10.0"
- languageName: unknown
- linkType: soft
-
-"detect-indent@npm:^7.0.1":
- version: 7.0.1
- resolution: "detect-indent@npm:7.0.1"
- checksum: 10c0/47b6e3e3dda603c386e73b129f3e84844ae59bc2615f5072becf3cc02eab400bed5a4e6379c49d0b18cf630e80c2b07e87e0038b777addbc6ef793ad77dd05bc
- languageName: node
- linkType: hard
-
-"detect-newline@npm:^4.0.0":
- version: 4.0.0
- resolution: "detect-newline@npm:4.0.0"
- checksum: 10c0/87dcff7a9ec25d1f4b356c068c3f05eb68bf6c2cbc4461da013df317ec184bbc96a2383bfaab9f963882ab988336bdadd5ce71b9cec55dde02d8ef84cef99250
- languageName: node
- linkType: hard
-
-"dir-glob@npm:^3.0.1":
- version: 3.0.1
- resolution: "dir-glob@npm:3.0.1"
- dependencies:
- path-type: "npm:^4.0.0"
- checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c
- languageName: node
- linkType: hard
-
-"fast-glob@npm:^3.3.0":
- version: 3.3.0
- resolution: "fast-glob@npm:3.3.0"
- dependencies:
- "@nodelib/fs.stat": "npm:^2.0.2"
- "@nodelib/fs.walk": "npm:^1.2.3"
- glob-parent: "npm:^5.1.2"
- merge2: "npm:^1.3.0"
- micromatch: "npm:^4.0.4"
- checksum: 10c0/4700063a2d7c9aae178f575648580bee1fc3f02ab3f358236d77811f52332bc10a398e75c6d5ecde61216996f3308247b37d70e2ee605a0748abe147f01b8f64
- languageName: node
- linkType: hard
-
-"fastq@npm:^1.6.0":
- version: 1.13.0
- resolution: "fastq@npm:1.13.0"
- dependencies:
- reusify: "npm:^1.0.4"
- checksum: 10c0/76c7b5dafb93c7e74359a3e6de834ce7a7c2e3a3184050ed4cb652661de55cf8d4895178d8d3ccd23069395056c7bb15450660d38fb382ca88c142b22694d7c9
- languageName: node
- linkType: hard
-
-"fill-range@npm:^7.1.1":
- version: 7.1.1
- resolution: "fill-range@npm:7.1.1"
- dependencies:
- to-regex-range: "npm:^5.0.1"
- checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018
- languageName: node
- linkType: hard
-
-"get-stdin@npm:^9.0.0":
- version: 9.0.0
- resolution: "get-stdin@npm:9.0.0"
- checksum: 10c0/7ef2edc0c81a0644ca9f051aad8a96ae9373d901485abafaabe59fd347a1c378689d8a3d8825fb3067415d1d09dfcaa43cb9b9516ecac6b74b3138b65a8ccc6b
- languageName: node
- linkType: hard
-
-"git-hooks-list@npm:^3.0.0":
- version: 3.1.0
- resolution: "git-hooks-list@npm:3.1.0"
- checksum: 10c0/f1b93dd11b80b2a687b99a8bb553c0d07f344532d475b3ac2a5ff044d40fa71567ddcfa5cb39fae0b4e43a670a33f02f71ec3b24b7263233f3a3df89deddfb5a
- languageName: node
- linkType: hard
-
-"glob-parent@npm:^5.1.2":
- version: 5.1.2
- resolution: "glob-parent@npm:5.1.2"
- dependencies:
- is-glob: "npm:^4.0.1"
- checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee
- languageName: node
- linkType: hard
-
-"globby@npm:^13.1.2":
- version: 13.2.2
- resolution: "globby@npm:13.2.2"
- dependencies:
- dir-glob: "npm:^3.0.1"
- fast-glob: "npm:^3.3.0"
- ignore: "npm:^5.2.4"
- merge2: "npm:^1.4.1"
- slash: "npm:^4.0.0"
- checksum: 10c0/a8d7cc7cbe5e1b2d0f81d467bbc5bc2eac35f74eaded3a6c85fc26d7acc8e6de22d396159db8a2fc340b8a342e74cac58de8f4aee74146d3d146921a76062664
- languageName: node
- linkType: hard
-
-"ignore@npm:^5.2.4":
- version: 5.2.4
- resolution: "ignore@npm:5.2.4"
- checksum: 10c0/7c7cd90edd9fea6e037f9b9da4b01bf0a86b198ce78345f9bbd983929d68ff14830be31111edc5d70c264921f4962404d75b7262b4d9cc3bc12381eccbd03096
- languageName: node
- linkType: hard
-
-"is-extglob@npm:^2.1.1":
- version: 2.1.1
- resolution: "is-extglob@npm:2.1.1"
- checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912
- languageName: node
- linkType: hard
-
-"is-glob@npm:^4.0.1":
- version: 4.0.3
- resolution: "is-glob@npm:4.0.3"
- dependencies:
- is-extglob: "npm:^2.1.1"
- checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a
- languageName: node
- linkType: hard
-
-"is-number@npm:^7.0.0":
- version: 7.0.0
- resolution: "is-number@npm:7.0.0"
- checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811
- languageName: node
- linkType: hard
-
-"is-plain-obj@npm:^4.1.0":
- version: 4.1.0
- resolution: "is-plain-obj@npm:4.1.0"
- checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e
- languageName: node
- linkType: hard
-
-"lru-cache@npm:^6.0.0":
- version: 6.0.0
- resolution: "lru-cache@npm:6.0.0"
- dependencies:
- yallist: "npm:^4.0.0"
- checksum: 10c0/cb53e582785c48187d7a188d3379c181b5ca2a9c78d2bce3e7dee36f32761d1c42983da3fe12b55cb74e1779fa94cdc2e5367c028a9b35317184ede0c07a30a9
- languageName: node
- linkType: hard
-
-"merge2@npm:^1.3.0, merge2@npm:^1.4.1":
- version: 1.4.1
- resolution: "merge2@npm:1.4.1"
- checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb
- languageName: node
- linkType: hard
-
-"micromatch@npm:^4.0.4":
- version: 4.0.5
- resolution: "micromatch@npm:4.0.5"
- dependencies:
- braces: "npm:^3.0.2"
- picomatch: "npm:^2.3.1"
- checksum: 10c0/3d6505b20f9fa804af5d8c596cb1c5e475b9b0cd05f652c5b56141cf941bd72adaeb7a436fda344235cef93a7f29b7472efc779fcdb83b478eab0867b95cdeff
- languageName: node
- linkType: hard
-
-"path-type@npm:^4.0.0":
- version: 4.0.0
- resolution: "path-type@npm:4.0.0"
- checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c
- languageName: node
- linkType: hard
-
-"picomatch@npm:^2.3.1":
- version: 2.3.1
- resolution: "picomatch@npm:2.3.1"
- checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be
- languageName: node
- linkType: hard
-
-"queue-microtask@npm:^1.2.2":
- version: 1.2.3
- resolution: "queue-microtask@npm:1.2.3"
- checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102
- languageName: node
- linkType: hard
-
-"reusify@npm:^1.0.4":
- version: 1.0.4
- resolution: "reusify@npm:1.0.4"
- checksum: 10c0/c19ef26e4e188f408922c46f7ff480d38e8dfc55d448310dfb518736b23ed2c4f547fb64a6ed5bdba92cd7e7ddc889d36ff78f794816d5e71498d645ef476107
- languageName: node
- linkType: hard
-
-"run-parallel@npm:^1.1.9":
- version: 1.2.0
- resolution: "run-parallel@npm:1.2.0"
- dependencies:
- queue-microtask: "npm:^1.2.2"
- checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39
- languageName: node
- linkType: hard
-
-"semver@npm:^7.6.0":
- version: 7.6.0
- resolution: "semver@npm:7.6.0"
- dependencies:
- lru-cache: "npm:^6.0.0"
- bin:
- semver: bin/semver.js
- checksum: 10c0/fbfe717094ace0aa8d6332d7ef5ce727259815bd8d8815700853f4faf23aacbd7192522f0dc5af6df52ef4fa85a355ebd2f5d39f554bd028200d6cf481ab9b53
- languageName: node
- linkType: hard
-
-"slash@npm:^4.0.0":
- version: 4.0.0
- resolution: "slash@npm:4.0.0"
- checksum: 10c0/b522ca75d80d107fd30d29df0549a7b2537c83c4c4ecd12cd7d4ea6c8aaca2ab17ada002e7a1d78a9d736a0261509f26ea5b489082ee443a3a810586ef8eff18
- languageName: node
- linkType: hard
-
-"sort-object-keys@npm:^1.1.3":
- version: 1.1.3
- resolution: "sort-object-keys@npm:1.1.3"
- checksum: 10c0/3bf62398658d3ff4bbca0db4ed8f42f98abc41433859f63d02fb0ab953fbe5526be240ec7e5d85aa50fcab6c937f3fa7015abf1ecdeb3045a2281c53953886bf
- languageName: node
- linkType: hard
-
-"sort-package-json@npm:2.10.0":
- version: 2.10.0
- resolution: "sort-package-json@npm:2.10.0"
- dependencies:
- detect-indent: "npm:^7.0.1"
- detect-newline: "npm:^4.0.0"
- get-stdin: "npm:^9.0.0"
- git-hooks-list: "npm:^3.0.0"
- globby: "npm:^13.1.2"
- is-plain-obj: "npm:^4.1.0"
- semver: "npm:^7.6.0"
- sort-object-keys: "npm:^1.1.3"
- bin:
- sort-package-json: cli.js
- checksum: 10c0/f3325c402cd63fa42947e3861fde0ed26c742bb1db9011d4a4111f2a27427ec778ce8223af5c5dd8fcdb1cf49a1ff55d7e5323fb187d29811cd99e503a80fe26
- languageName: node
- linkType: hard
-
-"to-regex-range@npm:^5.0.1":
- version: 5.0.1
- resolution: "to-regex-range@npm:5.0.1"
- dependencies:
- is-number: "npm:^7.0.0"
- checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892
- languageName: node
- linkType: hard
-
-"tunnel@npm:^0.0.6":
- version: 0.0.6
- resolution: "tunnel@npm:0.0.6"
- checksum: 10c0/e27e7e896f2426c1c747325b5f54efebc1a004647d853fad892b46d64e37591ccd0b97439470795e5262b5c0748d22beb4489a04a0a448029636670bfd801b75
- languageName: node
- linkType: hard
-
-"uuid@npm:^8.3.2":
- version: 8.3.2
- resolution: "uuid@npm:8.3.2"
- bin:
- uuid: dist/bin/uuid
- checksum: 10c0/bcbb807a917d374a49f475fae2e87fdca7da5e5530820ef53f65ba1d12131bd81a92ecf259cc7ce317cbe0f289e7d79fdfebcef9bfa3087c8c8a2fa304c9be54
- languageName: node
- linkType: hard
-
-"yallist@npm:^4.0.0":
- version: 4.0.0
- resolution: "yallist@npm:4.0.0"
- checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a
- languageName: node
- linkType: hard
From b774874f6b3c43dc3092efed0f36ca5d6392fc03 Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Fri, 16 Aug 2024 18:30:58 +0100
Subject: [PATCH 35/52] chore(docs): update prettier config and format docs
content (#11293)
As title. I switched back to js vs json prettier configs so we can
extend one from another.
---
.prettierignore | 5 +-
.prettierrc.json | 12 -
docs/README.md | 3 +-
docs/docs/app-configuration-redwood-toml.md | 42 ++-
docs/docs/assets-and-files.md | 16 +-
docs/docs/auth/azure.md | 9 +-
docs/docs/auth/clerk.md | 4 +-
docs/docs/auth/custom.md | 18 +-
docs/docs/auth/dbauth.md | 36 +-
docs/docs/auth/firebase.md | 14 +-
docs/docs/auth/supabase.md | 55 +--
docs/docs/auth/supertokens.md | 2 +-
docs/docs/authentication.md | 5 -
docs/docs/builds.md | 9 +-
docs/docs/cells.md | 17 +-
docs/docs/cli-commands.md | 198 +++++------
docs/docs/connection-pooling.md | 19 +-
docs/docs/contributing-overview.md | 55 +--
docs/docs/contributing-walkthrough.md | 56 ++-
docs/docs/cors.md | 22 +-
docs/docs/create-redwood-app.md | 23 +-
docs/docs/data-migrations.md | 33 +-
docs/docs/database-seeds.md | 38 +-
docs/docs/deploy/baremetal.md | 86 ++---
docs/docs/deploy/coherence.md | 1 +
docs/docs/deploy/edgio.md | 19 +-
docs/docs/deploy/flightcontrol.md | 10 +-
docs/docs/deploy/introduction.md | 3 +-
docs/docs/deploy/netlify.md | 1 -
docs/docs/deploy/render.md | 1 +
docs/docs/deploy/serverless.md | 20 +-
docs/docs/deploy/vercel.md | 8 +-
docs/docs/directives.md | 34 +-
docs/docs/docker.md | 66 ++--
docs/docs/environment-variables.md | 4 +-
docs/docs/forms.md | 61 ++--
docs/docs/graphql.md | 194 +++++-----
docs/docs/graphql/caching.md | 10 +-
docs/docs/graphql/fragments.md | 63 ++--
docs/docs/graphql/mocking-graphql-requests.md | 29 +-
docs/docs/graphql/trusted-documents.md | 64 +++-
docs/docs/how-to/background-worker.md | 3 +-
.../build-dashboards-fast-with-tremor.md | 83 +++--
docs/docs/how-to/custom-function.md | 2 +-
docs/docs/how-to/dbauth-passwordless.md | 81 +++--
docs/docs/how-to/disable-api-database.md | 1 +
docs/docs/how-to/file-uploads.md | 4 +-
.../how-to/mocking-graphql-in-storybook.md | 30 +-
docs/docs/how-to/oauth.md | 22 +-
docs/docs/how-to/pagination.md | 3 +-
docs/docs/how-to/role-based-access-control.md | 28 +-
docs/docs/how-to/self-hosting-redwood.md | 1 +
docs/docs/how-to/sending-emails.md | 2 +-
docs/docs/how-to/supabase-auth.md | 3 +-
docs/docs/how-to/test-in-github-actions.md | 22 +-
docs/docs/how-to/using-a-third-party-api.md | 34 +-
docs/docs/how-to/using-gitpod.md | 8 +-
docs/docs/how-to/using-nvm.md | 7 +-
docs/docs/how-to/using-yarn.md | 1 +
docs/docs/how-to/windows-development-setup.md | 7 +-
docs/docs/intro-to-servers.md | 44 +--
docs/docs/local-postgres-setup.md | 39 +-
docs/docs/logger.md | 12 +-
docs/docs/mailer.md | 35 +-
docs/docs/monitoring/sentry.md | 6 +-
docs/docs/prerender.md | 23 +-
.../project-configuration-dev-test-build.mdx | 22 +-
docs/docs/quick-start.md | 4 +-
docs/docs/realtime.md | 66 ++--
docs/docs/redwoodrecord.md | 39 +-
docs/docs/router.md | 15 +-
docs/docs/schema-relations.md | 20 +-
docs/docs/security.md | 41 ++-
docs/docs/serverless-functions.md | 161 +++++----
docs/docs/services.md | 334 ++++++++++--------
docs/docs/storybook.md | 13 +-
docs/docs/studio.md | 27 +-
docs/docs/testing.md | 236 +++++++------
docs/docs/tutorial/afterword.md | 1 -
.../docs/tutorial/chapter0/what-is-redwood.md | 62 ++--
docs/docs/tutorial/chapter1/file-structure.md | 3 +
docs/docs/tutorial/chapter1/first-page.md | 15 +-
docs/docs/tutorial/chapter1/installation.md | 3 +-
docs/docs/tutorial/chapter2/cells.md | 12 +-
.../docs/tutorial/chapter2/getting-dynamic.md | 4 +-
docs/docs/tutorial/chapter2/routing-params.md | 9 +-
docs/docs/tutorial/chapter3/forms.md | 40 +--
docs/docs/tutorial/chapter3/saving-data.md | 48 ++-
docs/docs/tutorial/chapter4/authentication.md | 45 +--
docs/docs/tutorial/chapter4/deployment.md | 9 +-
docs/docs/tutorial/chapter5/first-story.md | 1 -
docs/docs/tutorial/chapter5/first-test.md | 27 +-
docs/docs/tutorial/chapter5/storybook.md | 14 +-
docs/docs/tutorial/chapter5/testing.md | 4 +-
docs/docs/tutorial/chapter6/comment-form.md | 22 +-
.../docs/tutorial/chapter6/comments-schema.md | 83 ++---
.../tutorial/chapter6/multiple-comments.md | 31 +-
.../docs/tutorial/chapter6/the-redwood-way.md | 4 +-
.../tutorial/chapter7/api-side-currentuser.md | 53 ++-
docs/docs/tutorial/chapter7/rbac.md | 35 +-
docs/docs/tutorial/intermission.md | 2 +-
docs/docs/typescript/generated-types.md | 13 +-
docs/docs/typescript/introduction.md | 4 +-
docs/docs/typescript/strict-mode.md | 16 +-
docs/docs/typescript/utility-types.md | 62 ++--
docs/docs/vite-configuration.md | 16 +-
docs/docs/webhooks.md | 5 +-
docs/prettier.config.js | 4 -
docs/prettier.config.mjs | 12 +
docs/src/css/custom.css | 14 +-
docs/tsconfig.json | 2 +-
docs/versions.json | 10 +-
prettier.config.mjs | 17 +
113 files changed, 1919 insertions(+), 1627 deletions(-)
delete mode 100644 .prettierrc.json
delete mode 100644 docs/prettier.config.js
create mode 100644 docs/prettier.config.mjs
create mode 100644 prettier.config.mjs
diff --git a/.prettierignore b/.prettierignore
index d542685cc9d2..2a65829322f1 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -4,8 +4,9 @@
# Ignore fixture projects
__fixtures__
-# Ignore the docs (docusaurus) project
-docs/
+# Ignore the certain files in /docs
+/docs/versioned_docs
+/docs/versioned_sidebars
# Ignore the .nx directory
/.nx
diff --git a/.prettierrc.json b/.prettierrc.json
deleted file mode 100644
index ef04338a6774..000000000000
--- a/.prettierrc.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "$schema": "http://json.schemastore.org/prettierrc",
- "bracketSpacing": true,
- "tabWidth": 2,
- "semi": false,
- "singleQuote": true,
- "plugins": [
- "prettier-plugin-curly",
- "prettier-plugin-sh",
- "prettier-plugin-packagejson"
- ]
-}
diff --git a/docs/README.md b/docs/README.md
index 7f694bc3c9f8..8c47a8c4e6bc 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -20,7 +20,7 @@ After running `yarn start`, you should be able to see your changes in the local
#### Internal linking
-For links to other docs inside the `tutorials` directory you need to use *relative* links.
+For links to other docs inside the `tutorials` directory you need to use _relative_ links.
```
In [previous section](./first-page) we....
@@ -35,6 +35,7 @@ Fork the repo, make your changes and open a PR on GitHub. That will build and la
Double check that your changes look good!
### Updating Doc Images
+
To update any images in the doc, first upload your screenshot into a comment textbox in your PR. Once it's uploaded, you can open the image in a new tab and use the github url as a image link in your docs.
## Contributors
diff --git a/docs/docs/app-configuration-redwood-toml.md b/docs/docs/app-configuration-redwood-toml.md
index 214a17d0c481..afb3abbdd613 100644
--- a/docs/docs/app-configuration-redwood-toml.md
+++ b/docs/docs/app-configuration-redwood-toml.md
@@ -28,17 +28,17 @@ For certain options, instead of having to configure build tools directly, there'
## [web]
-| Key | Description | Default |
-| :---------------------------- | :--------------------------------------------------------- | :---------------------- |
-| `title` | Title of your Redwood app | `'Redwood App'` |
-| `port` | Port for the web server to listen at | `8910` |
-| `apiUrl` | URL to your api server. This can be a relative URL in which case it acts like a proxy, or a fully-qualified URL | `'/.redwood/functions'` |
-| `includeEnvironmentVariables` | Environment variables made available to the web side during dev and build | `[]` |
-| `host` | Hostname for the web server to listen at | Defaults to `'0.0.0.0'` in production and `'::'` in development |
-| `apiGraphQLUrl` | URL to your GraphQL function | `'${apiUrl}/graphql'` |
-| `apiDbAuthUrl` | URL to your dbAuth function | `'${apiUrl}/auth'` |
-| `sourceMap` | Enable source maps for production builds | `false` |
-| `a11y` | Enable storybook `addon-a11y` and `eslint-plugin-jsx-a11y` | `true` |
+| Key | Description | Default |
+| :---------------------------- | :-------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- |
+| `title` | Title of your Redwood app | `'Redwood App'` |
+| `port` | Port for the web server to listen at | `8910` |
+| `apiUrl` | URL to your api server. This can be a relative URL in which case it acts like a proxy, or a fully-qualified URL | `'/.redwood/functions'` |
+| `includeEnvironmentVariables` | Environment variables made available to the web side during dev and build | `[]` |
+| `host` | Hostname for the web server to listen at | Defaults to `'0.0.0.0'` in production and `'::'` in development |
+| `apiGraphQLUrl` | URL to your GraphQL function | `'${apiUrl}/graphql'` |
+| `apiDbAuthUrl` | URL to your dbAuth function | `'${apiUrl}/auth'` |
+| `sourceMap` | Enable source maps for production builds | `false` |
+| `a11y` | Enable storybook `addon-a11y` and `eslint-plugin-jsx-a11y` | `true` |
### Customizing the GraphQL Endpoint
@@ -105,22 +105,20 @@ Don't make secrets available to your web side. Everything in `includeEnvironment
## [api]
-| Key | Description | Default |
-| :------------- | :---------------------------------- | :------------------------- |
-| `port` | Port for the api server to listen at | `8911` |
-| `host` | Hostname for the api server to listen at | Defaults to `'0.0.0.0'` in production and `'::'` in development |
-| `schemaPath` | The location of your Prisma schema. If you have [enabled Prisma multi file schemas](https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema), then its value is the directory where your `schema.prisma` can be found, for example: `'./api/db/schema'` | Defaults to `'./api/db/schema.prisma'` |
-| `debugPort` | Port for the debugger to listen at | `18911` |
-
+| Key | Description | Default |
+| :----------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- |
+| `port` | Port for the api server to listen at | `8911` |
+| `host` | Hostname for the api server to listen at | Defaults to `'0.0.0.0'` in production and `'::'` in development |
+| `schemaPath` | The location of your Prisma schema. If you have [enabled Prisma multi file schemas](https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema), then its value is the directory where your `schema.prisma` can be found, for example: `'./api/db/schema'` | Defaults to `'./api/db/schema.prisma'` |
+| `debugPort` | Port for the debugger to listen at | `18911` |
Additional server configuration can be done using [Server File](docker.md#using-the-server-file)
-
### Multi File Schema
-Prisma's `prismaSchemaFolder` [feature](https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema) allows you to define multiple files in a schema subdirectory of your prisma directory.
+Prisma's `prismaSchemaFolder` [feature](https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema) allows you to define multiple files in a schema subdirectory of your prisma directory.
-:::note Important
+:::note Important
If you wish to [organize your Prisma Schema into multiple files](https://www.prisma.io/blog/organize-your-prisma-schema-with-multi-file-support), you will need [enable](https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema) that feature in Prisma, move your `schema.prisma` file into a new directory such as `./api/db/schema` and then set `schemaPath` in the api toml config.
:::
@@ -128,7 +126,7 @@ For example:
```toml title="redwood.toml"
[api]
- port = 8911
+ port = 8911
schemaPath = "./api/db/schema"
```
diff --git a/docs/docs/assets-and-files.md b/docs/docs/assets-and-files.md
index 785f92861ef5..2cdcf544242c 100644
--- a/docs/docs/assets-and-files.md
+++ b/docs/docs/assets-and-files.md
@@ -144,8 +144,8 @@ export const CarIcon = (props: SVGProps) => {
If you needed to convert a whole library of SVGs into stylable (or animatable!) components, one easy way would be to use the [SVGR cli](https://react-svgr.com/docs/cli/)
-
## Custom fonts
+
There are many different ways to peel this potato – it's all a search away – but if you're using the CSS `@font-face` rule, we have a quick tip for you:
1. Place your fonts in the public folder, so it gets carried across
@@ -154,15 +154,15 @@ There are many different ways to peel this potato – it's all a search away –
```shell
web/
├── src
- ├── App.tsx
- ├── entry.client.tsx
- ├── index.css
- ├── ...
+├── App.tsx
+├── entry.client.tsx
+├── index.css
+├── ...
├── public
-│ ├── favicon.png
-│ ├── fonts
+│ ├── favicon.png
+│ ├── fonts
// highlight-next-line
-│ │ └── RedwoodNeue.woff2
+│ │ └── RedwoodNeue.woff2
```
```css
diff --git a/docs/docs/auth/azure.md b/docs/docs/auth/azure.md
index cf46c4d44f88..61477529fcb6 100644
--- a/docs/docs/auth/azure.md
+++ b/docs/docs/auth/azure.md
@@ -94,9 +94,7 @@ const HomePage = () => {
{/* MetaTags, h1, paragraphs, etc. */}
{JSON.stringify({ isAuthenticated })}
-
+
>
)
}
@@ -155,7 +153,7 @@ AZURE_ACTIVE_DIRECTORY_KNOWN_AUTHORITY=https://rwauthtestb2c.b2clogin.com
```
And don't forget to add `AZURE_ACTIVE_DIRECTORY_KNOWN_AUTHORITY` to the `includeEnvironmentVariables` array in `redwood.toml`.
-(`AZURE_ACTIVE_DIRECTORY_JWT_ISSUER` is only used on the API side. But more importantly, it's sensitive—do *not* include it in the web side.)
+(`AZURE_ACTIVE_DIRECTORY_JWT_ISSUER` is only used on the API side. But more importantly, it's sensitive—do _not_ include it in the web side.)
#### Update `activeDirectoryClient` instance
@@ -170,7 +168,7 @@ const azureActiveDirectoryClient = new PublicClientApplication({
postLogoutRedirectUri:
process.env.AZURE_ACTIVE_DIRECTORY_LOGOUT_REDIRECT_URI,
// highlight-next-line
- knownAuthorities: [process.env.AZURE_ACTIVE_DIRECTORY_KNOWN_AUTHORITY]
+ knownAuthorities: [process.env.AZURE_ACTIVE_DIRECTORY_KNOWN_AUTHORITY],
},
})
```
@@ -178,5 +176,6 @@ const azureActiveDirectoryClient = new PublicClientApplication({
Now you can call the `logIn` and `logOut` functions from `useAuth()`, and everything should just work.
Here's a few more links to relevant documentation for reference:
+
- [Overview of tokens in Azure Active Directory B2C](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tokens-overview)
- [Working with MSAL.js and Azure AD B2C](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/working-with-b2c.md)
diff --git a/docs/docs/auth/clerk.md b/docs/docs/auth/clerk.md
index 06266d0b4774..ec0f98555c50 100644
--- a/docs/docs/auth/clerk.md
+++ b/docs/docs/auth/clerk.md
@@ -13,7 +13,6 @@ See the [migration guide](https://github.com/redwoodjs/redwood/releases/tag/v5.3
:::
-
To get started, run the setup command:
```text
@@ -83,7 +82,6 @@ const HomePage = () => {
Clicking sign up should open a sign-up box and after you sign up, you should see `{"isAuthenticated":true}` on the page.
-
## Customizing the session token
There's not a lot to the default session token.
@@ -105,7 +103,7 @@ export const getCurrentUser = async (
// ...
}
-````
+```
## Avoiding feature duplication
diff --git a/docs/docs/auth/custom.md b/docs/docs/auth/custom.md
index 65b5b801ab11..a694585d5ddf 100644
--- a/docs/docs/auth/custom.md
+++ b/docs/docs/auth/custom.md
@@ -71,7 +71,7 @@ We'll take all the work you have to do reading docs for granted here and cut to
import { NhostClient } from '@nhost/nhost-js'
const client = new NhostClient({
- backendUrl: '...'
+ backendUrl: '...',
})
```
@@ -105,7 +105,7 @@ import { NhostClient } from '@nhost/nhost-js'
// ...
const client = new NhostClient({
- backendUrl: process.env.NHOST_BACKEND_URL
+ backendUrl: process.env.NHOST_BACKEND_URL,
})
```
@@ -283,10 +283,16 @@ const HomePage = () => {
{/* MetaTags, h1, paragraphs, etc. */}
{JSON.stringify({ isAuthenticated })}
-
+
>
)
}
diff --git a/docs/docs/auth/dbauth.md b/docs/docs/auth/dbauth.md
index 7bb395831159..b4207909941c 100644
--- a/docs/docs/auth/dbauth.md
+++ b/docs/docs/auth/dbauth.md
@@ -121,7 +121,7 @@ Almost all config for dbAuth lives in `api/src/functions/auth.js` in the object
### allowedUserFields
```javascript
-allowedUserFields: ["id", "email"]
+allowedUserFields: ['id', 'email']
```
Most of the auth handlers accept a `user` argument that you can reference in the body of the function. These handlers also sometimes return that `user` object. As a security measure, `allowedUserFields` defines the only properties that will be available in that object so that sensitive data isn't accidentally leaked by these handlers to the client.
@@ -223,13 +223,16 @@ If the password is valid, return `true`. Otherwise, throw the `PasswordValidatio
```javascript
signup: {
passwordValidation: (password) => {
-
if (password.length < 8) {
- throw new PasswordValidationError('Password must be at least 8 characters')
+ throw new PasswordValidationError(
+ 'Password must be at least 8 characters'
+ )
}
if (!password.match(/[A-Z]/)) {
- throw new PasswordValidationError('Password must contain at least one capital letter')
+ throw new PasswordValidationError(
+ 'Password must contain at least one capital letter'
+ )
}
return true
@@ -299,13 +302,13 @@ login: {
By default no setting is required. This is because each db has its own rules for enabling this feature. To enable please see the table below and pick the correct 'userMatchString' for your db of choice.
-| DB | Default | usernameMatchString | notes |
-|---|---|---|---|
-| Postgres | 'default' | 'insensitive' | |
-| MySQL | 'case-insensitive' | N/A | turned on by default so no setting required |
-| MongoDB | 'default' | 'insensitive' |
-| SQLite | N/A | N/A | [Not Supported] Insensitive checks can only be defined at a per column level |
-| Microsoft SQL Server | 'case-insensitive' | N/A | turned on by default so no setting required |
+| DB | Default | usernameMatchString | notes |
+| -------------------- | ------------------ | ------------------- | ---------------------------------------------------------------------------- |
+| Postgres | 'default' | 'insensitive' | |
+| MySQL | 'case-insensitive' | N/A | turned on by default so no setting required |
+| MongoDB | 'default' | 'insensitive' |
+| SQLite | N/A | N/A | [Not Supported] Insensitive checks can only be defined at a per column level |
+| Microsoft SQL Server | 'case-insensitive' | N/A | turned on by default so no setting required |
### Cookie config
@@ -440,13 +443,13 @@ In both cases, actual scanning and matching of devices is handled by the operati
WebAuthn is supported in the following browsers (as of July 2022):
-| OS | Browser | Authenticator |
-| ------- | ------- | ------------- |
-| macOS | Firefox | Yubikey Security Key NFC (USB), Yubikey 5Ci, SoloKey |
+| OS | Browser | Authenticator |
+| ------- | ------- | -------------------------------------------------------------- |
+| macOS | Firefox | Yubikey Security Key NFC (USB), Yubikey 5Ci, SoloKey |
| macOS | Chrome | Touch ID, Yubikey Security Key NFC (USB), Yubikey 5Ci, SoloKey |
| iOS | All | Face ID, Touch ID, Yubikey Security Key NFC (NFC), Yubikey 5Ci |
-| Android | Chrome | Fingerprint Scanner, caBLE |
-| Android | Firefox | Screen PIN |
+| Android | Chrome | Fingerprint Scanner, caBLE |
+| Android | Firefox | Screen PIN |
### Configuration
@@ -528,7 +531,6 @@ import { db } from 'src/lib/db'
import { DbAuthHandler } from '@redwoodjs/api'
export const handler = async (event, context) => {
-
// assorted handler config here...
const authHandler = new DbAuthHandler(event, context, {
diff --git a/docs/docs/auth/firebase.md b/docs/docs/auth/firebase.md
index dc4b3a5d95aa..6fa20136e3e4 100644
--- a/docs/docs/auth/firebase.md
+++ b/docs/docs/auth/firebase.md
@@ -59,10 +59,16 @@ const HomePage = () => {
{/* MetaTags, h1, paragraphs, etc. */}
{JSON.stringify({ isAuthenticated })}
-
+
>
)
}
diff --git a/docs/docs/auth/supabase.md b/docs/docs/auth/supabase.md
index 5b4e19903f77..d9274e799773 100644
--- a/docs/docs/auth/supabase.md
+++ b/docs/docs/auth/supabase.md
@@ -1,6 +1,7 @@
---
sidebar_label: Supabase
---
+
# Supabase Authentication
To get started, run the setup command:
@@ -53,7 +54,6 @@ After you sign up, head to your inbox: there should be a confirmation email from
Click the link, then head back to your app.
Once you refresh the page, you should see `{"isAuthenticated":true}` on the page.
-
Let's make sure: if this is a brand new project, generate a home page.
There we'll try to sign up by destructuring `signUp` from the `useAuth` hook (import that from `'src/auth'`). We'll also destructure and display `isAuthenticated` to see if it worked:
@@ -69,10 +69,16 @@ const HomePage = () => {
{/* MetaTags, h1, paragraphs, etc. */}
{JSON.stringify({ isAuthenticated })}
-
+
>
)
}
@@ -126,14 +132,14 @@ Creates a new user with additional user metadata.
const { signUp } = useAuth()
await signUp({
-email: 'example@email.com',
+ email: 'example@email.com',
password: 'example-password',
options: {
data: {
first_name: 'John',
age: 27,
- }
- }
+ },
+ },
})
```
@@ -145,11 +151,11 @@ Creates a new user with a redirect URL.
const { signUp } = useAuth()
await signUp({
-email: 'example@email.com',
+ email: 'example@email.com',
password: 'example-password',
options: {
- emailRedirectTo: 'https://example.com/welcome'
- }
+ emailRedirectTo: 'https://example.com/welcome',
+ },
})
```
@@ -157,7 +163,7 @@ email: 'example@email.com',
Log in an existing user with an email and password or phone and password.
-* Requires either an email and password or a phone number and password.
+- Requires either an email and password or a phone number and password.
```ts
const { logIn } = useAuth()
@@ -173,9 +179,9 @@ await logIn({
Log in a user using magiclink or a one-time password (OTP).
-* Requires either an email or phone number.
+- Requires either an email or phone number.
-* This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
+- This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
```ts
const { logIn } = useAuth()
@@ -184,8 +190,8 @@ await logIn({
authMethod: 'otp',
email: 'example@email.com',
options: {
- emailRedirectTo: 'https://example.com/welcome'
- }
+ emailRedirectTo: 'https://example.com/welcome',
+ },
})
```
@@ -193,9 +199,9 @@ await logIn({
Log in an existing user via a third-party provider.
-* This method is used for signing in using a third-party provider.
+- This method is used for signing in using a third-party provider.
-* Supabase supports many different [third-party providers](https://supabase.com/docs/guides/auth#providers).
+- Supabase supports many different [third-party providers](https://supabase.com/docs/guides/auth#providers).
```ts
const { logIn } = useAuth()
@@ -270,10 +276,9 @@ logOut()
Log in a user given a User supplied OTP received via mobile.
-* The verifyOtp method takes in different verification types. If a phone number is used, the type can either be sms or phone_change. If an email address is used, the type can be one of the following: signup, magiclink, recovery, invite or email_change.
-
-* The verification type used should be determined based on the corresponding auth method called before verifyOtp to sign up / sign-in a user.
+- The verifyOtp method takes in different verification types. If a phone number is used, the type can either be sms or phone_change. If an email address is used, the type can be one of the following: signup, magiclink, recovery, invite or email_change.
+- The verification type used should be determined based on the corresponding auth method called before verifyOtp to sign up / sign-in a user.
The RedwoodJS auth provider doesn't expose the `veriftyOtp` method from the Supabase SDK directly.
@@ -285,7 +290,7 @@ So, in order to use the `verifyOtp` method, you would:
const { client } = useAuth()
useEffect(() => {
- const { data, error } = await client.verifyOtp({ phone, token, type: 'sms'})
+ const { data, error } = await client.verifyOtp({ phone, token, type: 'sms' })
}, [client])
```
@@ -317,13 +322,15 @@ useEffect(() => {
Receive a notification every time an auth event happens.
-* Types of auth events: `SIGNED_IN`, `SIGNED_OUT`, `TOKEN_REFRESHED`, `USER_UPDATED`, `PASSWORD_RECOVERY`
+- Types of auth events: `SIGNED_IN`, `SIGNED_OUT`, `TOKEN_REFRESHED`, `USER_UPDATED`, `PASSWORD_RECOVERY`
```ts
const { client } = useAuth()
useEffect(() => {
- const { data: { subscription } } = client.onAuthStateChange((event, session) => {
+ const {
+ data: { subscription },
+ } = client.onAuthStateChange((event, session) => {
console.log(event, session)
})
diff --git a/docs/docs/auth/supertokens.md b/docs/docs/auth/supertokens.md
index 8b0b6b97ca8a..ecdc200726c6 100644
--- a/docs/docs/auth/supertokens.md
+++ b/docs/docs/auth/supertokens.md
@@ -51,6 +51,7 @@ SUPERTOKENS_API_KEY=your-api-key # The value can be omitted when self-hosting Su
```
## Social login setup
+
The following environment variables have to be set up (depending on the social login options):
```bash
@@ -79,7 +80,6 @@ includeEnvironmentVariables = [
]
```
-
# Page setup
Let's make sure: if this is a brand new project, generate a home page.
diff --git a/docs/docs/authentication.md b/docs/docs/authentication.md
index b680c802a934..7c3d96e5ab1e 100644
--- a/docs/docs/authentication.md
+++ b/docs/docs/authentication.md
@@ -128,7 +128,6 @@ const Routes = () => {
-
// highlight-next-line
@@ -150,16 +149,13 @@ const Routes = () => {
-
-
// highlight-next-line
-
// highlight-next-line
@@ -187,7 +183,6 @@ export const handler = createGraphQLHandler({
getCurrentUser,
// ...
})
-
```
If you're using one of Redwood's official integrations, `authDecoder` comes from the corresponding integration package (in auth0's case, `@redwoodjs/auth-auth0-api`):
diff --git a/docs/docs/builds.md b/docs/docs/builds.md
index 7303f1872814..b5cc87de5f0f 100644
--- a/docs/docs/builds.md
+++ b/docs/docs/builds.md
@@ -1,6 +1,7 @@
---
description: What happens when you build your app
---
+
# Builds
> ⚠ **Work in Progress** ⚠️
@@ -11,7 +12,6 @@ description: What happens when you build your app
> You can edit this doc [here](https://github.com/redwoodjs/redwoodjs.com/blob/main/docs/builds.md).
> If you have any questions, just ask for help! We're active on the [forums](https://community.redwoodjs.com/c/contributing/9) and on [discord](https://discord.com/channels/679514959968993311/747258086569541703).
-
## API
The api side of Redwood is transpiled by Babel into the `./api/dist` folder.
@@ -28,9 +28,10 @@ yarn zip-it-and-ship-it dist/functions/ zipballs/
Each lambda function in `./api/dist/functions` is parsed by zip-it-and-ship-it resulting in a zip file per lambda function that contains all the dependencies required for that lambda function.
->Note: The `@netlify/zip-it-and-ship-it` package needs to be installed as a dev dependency in `api/`. Use the command `yarn workspace api add -D @netlify/zip-it-and-ship-it`.
->- You can learn more about the package [here](https://www.npmjs.com/package/@netlify/zip-it-and-ship-it).
->- For more information on AWS Serverless Deploy see these [docs](/docs/deploy/serverless).
+> Note: The `@netlify/zip-it-and-ship-it` package needs to be installed as a dev dependency in `api/`. Use the command `yarn workspace api add -D @netlify/zip-it-and-ship-it`.
+>
+> - You can learn more about the package [here](https://www.npmjs.com/package/@netlify/zip-it-and-ship-it).
+> - For more information on AWS Serverless Deploy see these [docs](/docs/deploy/serverless).
## Web
diff --git a/docs/docs/cells.md b/docs/docs/cells.md
index 0a7377cd6b54..addfa2d6b2ac 100644
--- a/docs/docs/cells.md
+++ b/docs/docs/cells.md
@@ -1,6 +1,7 @@
---
description: Declarative data fetching with Cells
---
+
# Cells
Cells are a declarative approach to data fetching and one of Redwood's signature modes of abstraction.
@@ -43,7 +44,7 @@ yarn rw generate cell equipment --list
## Cells in-depth
-Cells exports five constants: `QUERY`, `Loading` , `Empty` , `Failure` and `Success`. The root query in `QUERY` is the same as `` so that, if you're generating a cell based on a model in your `schema.prisma`, you can get something out of the database right away. But there's a good chance you won't generate your Cell this way, so if you need to, make sure to change the root query. See the [Cells](tutorial/chapter2/cells.md#our-first-cell) section of the Tutorial for a great example of this.
+Cells exports five constants: `QUERY`, `Loading` , `Empty` , `Failure` and `Success`. The root query in `QUERY` is the same as `` so that, if you're generating a cell based on a model in your `schema.prisma`, you can get something out of the database right away. But there's a good chance you won't generate your Cell this way, so if you need to, make sure to change the root query. See the [Cells](tutorial/chapter2/cells.md#our-first-cell) section of the Tutorial for a great example of this.
## Usage
@@ -114,7 +115,7 @@ export default HomePage
```jsx {2-3}
export const QUERY = gql`
- query($numberToShow: Int!) {
+ query ($numberToShow: Int!) {
posts(numberToShow: $numberToShow) {
id
title
@@ -135,8 +136,8 @@ By default, `beforeQuery` gives any props passed from the parent component to `Q
export const beforeQuery = (props) => {
return {
variables: props,
- fetchPolicy: 'cache-and-network'
- }
+ fetchPolicy: 'cache-and-network',
+ }
}
```
@@ -156,8 +157,8 @@ export const beforeQuery = () => {
const { currentUser } = useAuth()
return {
- variables: { userId: currentUser.id }
- }
+ variables: { userId: currentUser.id },
+ }
}
```
@@ -224,6 +225,7 @@ But, like `Loading`, Storybook is probably a better place to develop this.
In production, failed cells won't break your app, they'll just be empty divs... -->
In this example, we use the `errorCode` to conditionally render the error heading title, and we also use it for our translation string.
+
```jsx
export const Failure = ({ error, errorCode }: CellFailureProps) => {
const { t } = useTranslation()
@@ -282,7 +284,7 @@ client = useApolloClient()
client.query({
query: gql`
...
- `
+ `,
})
```
@@ -347,6 +349,7 @@ And now let's say that Babel isn't going to come along and assemble our exports.
We'd probably do something like this:
+
```jsx
const QUERY = gql`
query {
diff --git a/docs/docs/cli-commands.md b/docs/docs/cli-commands.md
index f09be28f7a42..35afdca162ad 100644
--- a/docs/docs/cli-commands.md
+++ b/docs/docs/cli-commands.md
@@ -71,10 +71,10 @@ yarn redwood build [side..]
We use Babel to transpile the api side into `./api/dist` and Vite to package the web side into `./web/dist`.
-| Arguments & Options | Description |
-| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `side` | Which side(s) to build. Choices are `api` and `web`. Defaults to `api` and `web` |
-| `--verbose, -v` | Print more information while building |
+| Arguments & Options | Description |
+| :------------------ | :------------------------------------------------------------------------------- |
+| `side` | Which side(s) to build. Choices are `api` and `web`. Defaults to `api` and `web` |
+| `--verbose, -v` | Print more information while building |
#### Usage
@@ -88,9 +88,9 @@ Running `yarn redwood build` without any arguments generates the Prisma client a
~/redwood-app$ yarn redwood build
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood build
- ✔ Generating the Prisma client...
- ✔ Building "api"...
- ✔ Building "web"...
+✔ Generating the Prisma client...
+✔ Building "api"...
+✔ Building "web"...
Done in 17.37s.
```
@@ -192,10 +192,10 @@ yarn redwood dev [side..]
`yarn redwood dev api` starts the Redwood dev server and `yarn redwood dev web` starts the Vite dev server with Redwood's config.
-| Argument | Description |
-| :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `side` | Which dev server(s) to start. Choices are `api` and `web`. Defaults to `api` and `web` |
-| `--forward, --fwd` | String of one or more Vite Dev Server config options. See example usage below |
+| Argument | Description |
+| :----------------- | :------------------------------------------------------------------------------------- |
+| `side` | Which dev server(s) to start. Choices are `api` and `web`. Defaults to `api` and `web` |
+| `--forward, --fwd` | String of one or more Vite Dev Server config options. See example usage below |
#### Usage
@@ -208,10 +208,10 @@ $ /redwood-app/node_modules/.bin/redwood dev api
$ /redwood-app/node_modules/.bin/dev-server
15:04:51 api | Listening on http://localhost:8911
15:04:51 api | Watching /home/dominic/projects/redwood/redwood-app/api
-15:04:51 api |
-15:04:51 api | Now serving
-15:04:51 api |
-15:04:51 api | ► http://localhost:8911/graphql/
+15:04:51 api \
+ | 15:04:51 api | Now serving
+15:04:51 api \
+ | 15:04:51 api | ► http://localhost:8911/graphql/
```
Using `--forward` (alias `--fwd`), you can pass one or more Vite Dev Server [config options](https://vitejs.dev/guide/cli#vite). The following will run the dev server, set the port to `1234`, and disable automatic browser opening.
@@ -268,7 +268,6 @@ yarn redwood deploy serverless
| `--pack-only` | Only package the build for deployment |
| `--first-run` | Use this flag the first time you deploy. The first deploy wizard will walk you through configuring your web side to connect to the api side |
-
### deploy netlify
Build command for Netlify deploy
@@ -284,6 +283,7 @@ yarn redwood deploy netlify
| `--data-migrate, --dm` | Migrate the data in your database [default: "true"] |
#### Example
+
The following command will build, apply Prisma DB migrations, and skip data migrations.
```
@@ -316,6 +316,7 @@ yarn redwood deploy render
| `--serve` | Run server for api in production [default: "true"] |
#### Example
+
The following command will build the Web side for static-site CDN deployment.
```
@@ -343,6 +344,7 @@ yarn redwood deploy vercel
| `--data-migrate, --dm` | Migrate the data in your database [default: "true"] |
#### Example
+
The following command will build, apply Prisma DB migrations, and skip data migrations.
```
@@ -402,6 +404,7 @@ See [this how to](how-to/background-worker.md) for an example of using exec to r
Set up and run experimental features.
Some caveats:
+
- these features do not follow SemVer (may be breaking changes in minor and patch releases)
- these features may be deprecated or removed (anytime)
- your feedback is wanted and necessary!
@@ -411,6 +414,7 @@ For more information, including details about specific features, see this Redwoo
**Available Experimental Features**
View all that can be _set up_:
+
```
yarn redwood experimental --help
```
@@ -491,9 +495,9 @@ Generating a user cell:
~/redwood-app$ yarn redwood generate cell user
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood g cell user
- ✔ Generating cell files...
- ✔ Writing `./web/src/components/UserCell/UserCell.test.js`...
- ✔ Writing `./web/src/components/UserCell/UserCell.js`...
+✔ Generating cell files...
+✔ Writing $(./web/src/components/UserCell/UserCell.test.js)...
+✔ Writing $(./web/src/components/UserCell/UserCell.js)...
Done in 1.00s.
```
@@ -552,9 +556,9 @@ Generating a user component:
~/redwood-app$ yarn redwood generate component user
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood g component user
- ✔ Generating component files...
- ✔ Writing `./web/src/components/User/User.test.js`...
- ✔ Writing `./web/src/components/User/User.js`...
+✔ Generating component files...
+✔ Writing $(./web/src/components/User/User.test.js)...
+✔ Writing $(./web/src/components/User/User.js)...
Done in 1.02s.
```
@@ -660,8 +664,8 @@ Generating a `myDirective` directive using the interactive command:
yarn rw g directive myDirective
? What type of directive would you like to generate? › - Use arrow-keys. Return to submit.
-❯ Validator - Implement a validation: throw an error if criteria not met to stop execution
- Transformer - Modify values of fields or query responses
+❯ Validator - Implement a validation: throw an error if criteria not met to stop execution
+Transformer - Modify values of fields or query responses
```
### generate function
@@ -699,8 +703,8 @@ Generating a user function:
~/redwood-app$ yarn redwood generate function user
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood g function user
- ✔ Generating function files...
- ✔ Writing `./api/src/functions/user.js`...
+✔ Generating function files...
+✔ Writing $(./api/src/functions/user.js)...
Done in 16.04s.
```
@@ -769,9 +773,9 @@ Generating a user layout:
~/redwood-app$ yarn redwood generate layout user
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood g layout user
- ✔ Generating layout files...
- ✔ Writing `./web/src/layouts/UserLayout/UserLayout.test.js`...
- ✔ Writing `./web/src/layouts/UserLayout/UserLayout.js`...
+✔ Generating layout files...
+✔ Writing $(./web/src/layouts/UserLayout/UserLayout.test.js)...
+✔ Writing $(./web/src/layouts/UserLayout/UserLayout.js)...
Done in 1.00s.
```
@@ -809,14 +813,14 @@ See the [RedwoodRecord docs](redwoodrecord.md).
~/redwood-app$ yarn redwood generate model User
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood g model User
- ✔ Generating model file...
- ✔ Successfully wrote file `./api/src/models/User.js`
- ✔ Parsing datamodel, generating api/src/models/index.js...
+✔ Generating model file...
+✔ Successfully wrote file $(./api/src/models/User.js)
+✔ Parsing datamodel, generating api/src/models/index.js...
- Wrote /Users/rob/Sites/redwoodjs/redwood_record/.redwood/datamodel.json
- Wrote /Users/rob/Sites/redwoodjs/redwood_record/api/src/models/index.js
+Wrote /Users/rob/Sites/redwoodjs/redwood_record/.redwood/datamodel.json
+Wrote /Users/rob/Sites/redwoodjs/redwood_record/api/src/models/index.js
-✨ Done in 3.74s.
+✨ Done in 3.74s.
```
Generating a model automatically runs `yarn rw record init` as well.
@@ -826,7 +830,7 @@ Generating a model automatically runs `yarn rw record init` as well.
Generates a page component and updates the routes.
```bash
-yarn redwood generate page [path]
+yarn redwood generate page < name > [path]
```
`path` can include a route parameter which will be passed to the generated
@@ -944,6 +948,7 @@ const Routes = () => {
)
}
```
+
### generate realtime
Generate a boilerplate subscription or live query used with RedwoodJS Realtime.
@@ -952,12 +957,11 @@ Generate a boilerplate subscription or live query used with RedwoodJS Realtime.
yarn redwood generate realtime
```
-| Arguments & Options | Description |
-| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `name` | Name of the realtime event to setup.post` |
-| `-t, --type` | Choices: `liveQuery`, `subscription`. Optional. If not provided, you will be prompted to select.
-| `--force, -f` | Overwrite existing files
-
+| Arguments & Options | Description |
+| ------------------- | ------------------------------------------------------------------------------------------------ |
+| `name` | Name of the realtime event to setup.post` |
+| `-t, --type` | Choices: `liveQuery`, `subscription`. Optional. If not provided, you will be prompted to select. |
+| `--force, -f` | Overwrite existing files |
#### Usage
@@ -970,9 +974,9 @@ Generate a live query.
```bash
~/redwood-app$ yarn rw g realtime NewLiveQuery
? What type of realtime event would you like to create? › - Use arrow-keys. Return to submit.
-❯ Live Query
- Create a Live Query to watch for changes in data
- Subscription
+❯ Live Query
+Create a Live Query to watch for changes in data
+Subscription
✔ What type of realtime event would you like to create? › Live Query
✔ Checking for realtime environment prerequisites ...
@@ -985,8 +989,8 @@ Generate a subscription.
```bash
~/redwood-app$ yarn rw g realtime NewSub
? What type of realtime event would you like to create? › - Use arrow-keys. Return to submit.
- Live Query
-❯ Subscription - Create a Subscription to watch for events
+Live Query
+❯ Subscription - Create a Subscription to watch for events
✔ What type of realtime event would you like to create? › Subscription
✔ Checking for realtime environment prerequisites ...
@@ -994,7 +998,6 @@ Generate a subscription.
✔ Generating types ...
```
-
### generate scaffold
Generate Pages, SDL, and Services files based on a given DB schema Model. Also accepts ``.
@@ -1223,9 +1226,9 @@ yarn redwood g sdl --force --no-tests
~/redwood-app$ yarn redwood generate sdl user --force --no-tests
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood g sdl user
- ✔ Generating SDL files...
- ✔ Writing `./api/src/graphql/users.sdl.js`...
- ✔ Writing `./api/src/services/users/users.js`...
+✔ Generating SDL files...
+✔ Writing $(./api/src/graphql/users.sdl.js)...
+✔ Writing $(./api/src/services/users/users.js)...
Done in 1.04s.
```
@@ -1243,11 +1246,11 @@ Generating a user sdl:
~/redwood-app$ yarn redwood generate sdl user
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood g sdl user
- ✔ Generating SDL files...
- ✔ Writing `./api/src/graphql/users.sdl.js`...
- ✔ Writing `./api/src/services/users/users.scenarios.js`...
- ✔ Writing `./api/src/services/users/users.test.js`...
- ✔ Writing `./api/src/services/users/users.js`...
+✔ Generating SDL files...
+✔ Writing $(./api/src/graphql/users.sdl.js)...
+✔ Writing $(./api/src/services/users/users.scenarios.js)...
+✔ Writing $(./api/src/services/users/users.test.js)...
+✔ Writing $(./api/src/services/users/users.js)...
Done in 1.04s.
```
@@ -1387,7 +1390,6 @@ Services are where Redwood puts its business logic. They can be used by your Gra
| `--tests` | Generate test and scenario files [default: true] |
| `--rollback` | Rollback changes if an error occurs [default: true] |
-
**Destroying**
```
@@ -1402,10 +1404,10 @@ Generating a user service:
~/redwood-app$ yarn redwood generate service user
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/redwood g service user
- ✔ Generating service files...
- ✔ Writing `./api/src/services/users/users.scenarios.js`...
- ✔ Writing `./api/src/services/users/users.test.js`...
- ✔ Writing `./api/src/services/users/users.js`...
+✔ Generating service files...
+✔ Writing $(./api/src/services/users/users.scenarios.js)...
+✔ Writing $(./api/src/services/users/users.test.js)...
+✔ Writing $(./api/src/services/users/users.js)...
Done in 1.02s.
```
@@ -1839,9 +1841,9 @@ Copying the cell generator templates:
~/redwood-app$ yarn rw setup generator cell
yarn run v1.22.4
$ /redwood-app/node_modules/.bin/rw setup generator cell
- ✔ Copying generator templates...
- ✔ Wrote templates to /web/generators/cell
-✨ Done in 2.33s.
+✔ Copying generator templates...
+✔ Wrote templates to /web/generators/cell
+✨ Done in 2.33s.
```
### setup deploy (config)
@@ -1852,11 +1854,11 @@ Set up a deployment configuration.
yarn redwood setup deploy
```
-| Arguments & Options | Description |
-| :------------------ | :---------------------------------------------------------------------------------------------------- |
-| `provider` | Deploy provider to configure. Choices are `baremetal`, `coherence`, `edgio`, `flightcontrol`, `netlify`, `render`, `vercel`, or `aws-serverless [deprecated]`, |
-| `--database, -d` | Database deployment for Render only [choices: "none", "postgresql", "sqlite"] [default: "postgresql"] |
-| `--force, -f` | Overwrite existing configuration [default: false] |
+| Arguments & Options | Description |
+| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `provider` | Deploy provider to configure. Choices are `baremetal`, `coherence`, `edgio`, `flightcontrol`, `netlify`, `render`, `vercel`, or `aws-serverless [deprecated]`, |
+| `--database, -d` | Database deployment for Render only [choices: "none", "postgresql", "sqlite"] [default: "postgresql"] |
+| `--force, -f` | Overwrite existing configuration [default: false] |
#### setup deploy netlify
@@ -1902,10 +1904,10 @@ This command adds the necessary packages and files to get started using the Redw
yarn redwood setup mailer
```
-| Arguments & Options | Description |
-| :---------------------- | :----------------------------- |
-| `--force, -f` | Overwrite existing files |
-| `--skip-examples` | Do not include example content, such as a React email template |
+| Arguments & Options | Description |
+| :------------------ | :------------------------------------------------------------- |
+| `--force, -f` | Overwrite existing files |
+| `--skip-examples` | Do not include example content, such as a React email template |
### setup package
@@ -1917,18 +1919,20 @@ This command behaves similarly to `yarn dlx` but will attempt to confirm compati
yarn redwood setup package
```
-| Arguments & Options | Description |
-| :------------------ | :----------------------- |
+| Arguments & Options | Description |
+| :------------------ | :------------------------- |
| `--force, -f` | Forgo compatibility checks |
#### Usage
Run the made up `@redwoodjs/setup-example` package:
+
```bash
~/redwood-app$ yarn rw setup package @redwoodjs/setup-example
```
Run the same package but using a particular npm tag and avoiding any compatibility checks:
+
```bash
~/redwood-app$ yarn rw setup package @redwoodjs/setup-example@beta --force
```
@@ -1977,7 +1981,6 @@ Run `yarn rw setup graphql fragments`
This command creates the necessary configuration to start using [GraphQL Trusted Documents](./graphql/trusted-documents.md).
-
```
yarn redwood setup graphql trusted-documents
```
@@ -1995,7 +1998,6 @@ Run `yarn rw setup graphql trusted-documents`
✔ Configuring the GraphQL Handler to use a Trusted Documents store ...
```
-
If you have not setup the RedwoodJS server file, it will be setup:
```bash
@@ -2004,7 +2006,6 @@ If you have not setup the RedwoodJS server file, it will be setup:
✔ Adding required api packages...
```
-
### setup realtime
This command creates the necessary files, installs the required packages, and provides examples to setup RedwoodJS Realtime from GraphQL live queries and subscriptions. See the Realtime docs for more information.
@@ -2013,10 +2014,10 @@ This command creates the necessary files, installs the required packages, and pr
yarn redwood setup realtime
```
-| Arguments & Options | Description |
-| :------------------ | :----------------------- |
-| `-e, --includeExamples, --examples` | Include examples of how to implement liveQueries and subscriptions. Default: true. |
-| `--force, -f` | Forgo compatibility checks |
+| Arguments & Options | Description |
+| :---------------------------------- | :--------------------------------------------------------------------------------- |
+| `-e, --includeExamples, --examples` | Include examples of how to implement liveQueries and subscriptions. Default: true. |
+| `--force, -f` | Forgo compatibility checks |
:::note
@@ -2041,7 +2042,6 @@ Run `yarn rw setup realtime`
✔ Generating types ...
```
-
If you have not setup the RedwoodJS server file, it will be setup:
```bash
@@ -2062,8 +2062,6 @@ yarn redwood setup tsconfig
| :------------------ | :----------------------- |
| `--force, -f` | Overwrite existing files |
-
-
### setup ui
Set up a UI design or style library. Right now the choices are [TailwindCSS](https://tailwindcss.com/), [Chakra UI](https://chakra-ui.com/), and [Mantine UI](https://ui.mantine.dev/).
@@ -2072,10 +2070,10 @@ Set up a UI design or style library. Right now the choices are [TailwindCSS](htt
yarn rw setup ui
```
-| Arguments & Options | Description |
-| :------------------ | :-------------------------------------------------------------------------------------- |
+| Arguments & Options | Description |
+| :------------------ | :-------------------------------------------------------------------------- |
| `library` | Library to configure. Choices are `chakra-ui`, `tailwindcss`, and `mantine` |
-| `--force, -f` | Overwrite existing configuration |
+| `--force, -f` | Overwrite existing configuration |
## storybook
@@ -2105,15 +2103,15 @@ Run Jest tests for api and web.
yarn redwood test [side..]
```
-| Arguments & Options | Description |
-| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `sides or filter` | Which side(s) to test, and/or a regular expression to match against your test files to filter by |
-| `--help` | Show help |
-| `--version` | Show version number |
-| `--watch` | Run tests related to changed files based on hg/git (uncommitted files). Specify the name or path to a file to focus on a specific set of tests [default: true] |
-| `--watchAll` | Run all tests |
-| `--collectCoverage` | Show test coverage summary and output info to `coverage` directory in project root. See this directory for an .html coverage report |
-| `--clearCache` | Delete the Jest cache directory and exit without running tests |
+| Arguments & Options | Description |
+| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `sides or filter` | Which side(s) to test, and/or a regular expression to match against your test files to filter by |
+| `--help` | Show help |
+| `--version` | Show version number |
+| `--watch` | Run tests related to changed files based on hg/git (uncommitted files). Specify the name or path to a file to focus on a specific set of tests [default: true] |
+| `--watchAll` | Run all tests |
+| `--collectCoverage` | Show test coverage summary and output info to `coverage` directory in project root. See this directory for an .html coverage report |
+| `--clearCache` | Delete the Jest cache directory and exit without running tests |
| `--db-push` | Syncs the test database with your Prisma schema without requiring a migration. It creates a test database if it doesn't already exist [default: true]. This flag is ignored if your project doesn't have an `api` side. [👉 More details](#prisma-db-push). |
> **Note** all other flags are passed onto the jest cli. So for example if you wanted to update your snapshots you can pass the `-u` flag
@@ -2215,9 +2213,9 @@ Besides upgrading to a new stable release, you can use this command to upgrade t
A canary release is published to npm every time a PR is merged to the `main` branch, and when we're getting close to a new release, we publish release candidates.
-| Option | Description |
-| :-------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `--dry-run, -d` | Check for outdated packages without upgrading |
+| Option | Description |
+| :-------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `--dry-run, -d` | Check for outdated packages without upgrading |
| `--tag, -t` | Choices are "rc", "canary", "latest", "next", "experimental", or a specific version (e.g. "0.19.3"). WARNING: Unstable releases in the case of "canary", "rc", "next", and "experimental". And "canary" releases include breaking changes often requiring codemods if upgrading a project. |
#### Example
diff --git a/docs/docs/connection-pooling.md b/docs/docs/connection-pooling.md
index 750c012b3076..7f28eaed7c81 100644
--- a/docs/docs/connection-pooling.md
+++ b/docs/docs/connection-pooling.md
@@ -16,9 +16,9 @@ Production Redwood apps should enable connection pooling in order to properly sc
## Prisma Data Proxy
-The [Prisma Data Proxy](https://www.prisma.io/docs/data-platform/data-proxy) provides database connection management and pooling for Redwood apps using Prisma. It supports MySQL and Postgres databases in either the U.S. or EU regions.
+The [Prisma Data Proxy](https://www.prisma.io/docs/data-platform/data-proxy) provides database connection management and pooling for Redwood apps using Prisma. It supports MySQL and Postgres databases in either the U.S. or EU regions.
-To set up a Prisma Data Proxy, sign up for the [Prisma Data Platform](https://www.prisma.io/data-platform) for free. In your onboarding workflow, plug in the connection URL for your database and choose your region. This will generate a connection string for your app. Then follow the instructions in [Prisma's documentation](https://www.prisma.io/docs/concepts/data-platform/data-proxy).
+To set up a Prisma Data Proxy, sign up for the [Prisma Data Platform](https://www.prisma.io/data-platform) for free. In your onboarding workflow, plug in the connection URL for your database and choose your region. This will generate a connection string for your app. Then follow the instructions in [Prisma's documentation](https://www.prisma.io/docs/concepts/data-platform/data-proxy).
> Note that the example uses npm. Rather than using npm, you can access the Prisma CLI using `yarn redwood prisma` inside a Redwood app.
@@ -26,7 +26,6 @@ To set up a Prisma Data Proxy, sign up for the [Prisma Data Platform](https://ww
PgBouncer holds a connection pool to the database and proxies incoming client connections by sitting between Prisma Client and the database. This reduces the number of processes a database has to handle at any given time. PgBouncer passes on a limited number of connections to the database and queues additional connections for delivery when space becomes available.
-
To use Prisma Client with PgBouncer from a serverless function, add the `?pgbouncer=true` flag to the PostgreSQL connection URL:
```
@@ -57,12 +56,13 @@ postgresql://postgres:mydb.supabase.co:6543/postgres?sslmode=require&pgbouncer=t
```
## Heroku
+
For Postgres, see [Postgres Connection Pooling](https://devcenter.heroku.com/articles/postgres-connection-pooling).
Heroku does not officially support MySQL.
-
## Digital Ocean
+
For Postgres, see [How to Manage Connection Pools](https://www.digitalocean.com/docs/databases/postgresql/how-to/manage-connection-pools)
To run migrations through a connection pool, you're required to append connection parameters to your `DATABASE_URL`. Prisma needs to know to use pgbouncer (which is part of Digital Ocean's connection pool). If omitted, you may receive the following error:
@@ -77,7 +77,9 @@ To resolve this, use the following structure in your `DATABASE_URL`:
```
:25061/defaultdb?connection_limit=3&sslmode=require&pgbouncer=true&connect_timeout=10&pool_timeout=30
```
+
Here's a couple more things to be aware of:
+
- When using a Digital Ocean connection pool, you'll have multiple ports available. Typically the direct connection (without connection pooling) is on port `25060` and the connection through pgbouncer is served through port `25061`. Make sure you connect to your connection pool on port `25061`
- Adjust the `connection_limit`. Clusters provide 25 connections per 1 GB of RAM. Three connections per cluster are reserved for maintenance, and all remaining connections can be allocated to connection pools
- Both `pgbouncer=true` and `pool_timeout=30` are required to deploy successfully through your connection pool
@@ -85,20 +87,21 @@ Here's a couple more things to be aware of:
Connection Pooling for MySQL is not yet supported.
## AWS
+
Use [Amazon RDS Proxy](https://aws.amazon.com/rds/proxy) for MySQL or PostgreSQL.
From the [AWS Docs](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html#rds-proxy.limitations):
->Your RDS Proxy must be in the same VPC as the database. The proxy can't be publicly accessible.
-Because of this limitation, with out-of-the-box configuration, you can only use RDS Proxy if you're deploying your Lambda Functions to the same AWS account. Alternatively, you can use RDS directly, but you might require larger instances to handle your production traffic and the number of concurrent connections.
+> Your RDS Proxy must be in the same VPC as the database. The proxy can't be publicly accessible.
+Because of this limitation, with out-of-the-box configuration, you can only use RDS Proxy if you're deploying your Lambda Functions to the same AWS account. Alternatively, you can use RDS directly, but you might require larger instances to handle your production traffic and the number of concurrent connections.
## Why Connection Pooling?
Relational databases have a maximum number of concurrent client connections.
-* Postgres allows 100 by default
-* MySQL allows 151 by default
+- Postgres allows 100 by default
+- MySQL allows 151 by default
In a traditional server environment, you would need a large amount of traffic (and therefore web servers) to exhaust these connections, since each web server instance typically leverages a single connection.
diff --git a/docs/docs/contributing-overview.md b/docs/docs/contributing-overview.md
index 623d70ae02a3..873757c25733 100644
--- a/docs/docs/contributing-overview.md
+++ b/docs/docs/contributing-overview.md
@@ -26,13 +26,15 @@ _Before interacting with the Redwood community, please read and understand our [
> 3. 🪜 [Step-by-step Walkthrough](contributing-walkthrough.md) (including Video Recording)
> 4. 📈 [Current Project Status](https://github.com/orgs/redwoodjs/projects/11)
> 5. 🤔 What should I work on?
-> - [Good First Issue](https://redwoodjs.com/good-first-issue)
-> - [Discovery Process and Open Issues](#what-should-i-work-on)
+> - [Good First Issue](https://redwoodjs.com/good-first-issue)
+> - [Discovery Process and Open Issues](#what-should-i-work-on)
## The Characteristics of a Contributor
+
More than committing code, contributing is about human collaboration and relationship. Our community mantra is **“By helping each other be successful with Redwood, we make the Redwood project successful.”** We have a specific vision for the effect this project and community will have on you — it should give you superpowers to build+create, progress in skills, and help advance your career.
So who do you need to become to achieve this? Specifically, what characteristics, skills, and capabilities will you need to cultivate through practice? Here are our suggestions:
+
- Empathy
- Gratitude
- Generosity
@@ -42,6 +44,7 @@ All of these are applicable in relation to both others and yourself. The goal of
And you thought all this was just about opening a PR 🤣 Yes, it’s a super rewarding experience. But that’s just the beginning!
## What should I work on?
+
Even if you know the mechanics, it’s hard to get started without a starting place. Our best advice is this — dive into the Redwood Tutorial, read the docs, and build your own experiment with Redwood. Along the way, you’ll find typos, out-of-date (or missing) documentation, code that could work better, or even opportunities for improving and adding features. You’ll be engaging in the Forums and Chat and developing a feel for priorities and needs. This way, you’ll naturally follow your own interests and sooner than later intersect “things you’re interested in” + “ways to help improve Redwood”.
There are other more direct ways to get started as well, which are outlined below.
@@ -51,11 +54,13 @@ There are other more direct ways to get started as well, which are outlined belo
The Redwood Core Team is working publicly — progress is updated daily on the [Release Project Board](https://github.com/orgs/redwoodjs/projects/11).
Eventually, all this leads you back to Redwood’s GitHub Issues page. Here you’ll find open items that need help, which are organized by labels. There are four labels helpful for contributing:
+
1. [Good First Issue](https://github.com/redwoodjs/redwood/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22): these items are more likely to be an accessible entry point to the Framework. It’s less about skill level and more about focused scope.
2. [Help Wanted](https://github.com/redwoodjs/redwood/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22): these items especially need contribution help from the community.
3. [Bugs 🐛](https://github.com/redwoodjs/redwood/issues?q=is%3Aissue+is%3Aopen+label%3Abug%2Fconfirmed): last but not least, we always need help with bugs. Some are technically less challenging than others. Sometimes the best way you can help is to attempt to reproduce the bug and confirm whether or not it’s still an issue.
### Create a New Issue
+
Anyone can create a new Issue. If you’re not sure that your feature or idea is something to work on, start the discussion with an Issue. Describe the idea and problem + solution as clearly as possible, including examples or pseudo code if applicable. It’s also very helpful to `@` mention a maintainer or Core Team member that shares the area of interest.
Just know that there’s a lot of Issues that shuffle every day. If no one replies, it’s just because people are busy. Reach out in the Forums, Chat, or comment in the Issue. We intend to reply to every Issue that’s opened. If yours doesn’t have a reply, then give us a nudge!
@@ -73,26 +78,27 @@ For details on contributing to a specific package, see the package's README (lin
What you want to do not on the roadmap? Well, still go for it! We love spikes and proof-of-concepts. And if you have a question, just ask!
### RedwoodJS Framework Packages
-|Package|Description|
-|:-|:-|
-|[`@redwoodjs/api-server`](https://github.com/redwoodjs/redwood/blob/main/packages/api-server/README.md)|Run a Redwood app using Fastify server (alternative to serverless API)|
-|[`@redwoodjs/api`](https://github.com/redwoodjs/redwood/blob/main/packages/api/README.md)|Infrastructure components for your applications UI including logging, webhooks, authentication decoders and parsers, as well as tools to test custom serverless functions and webhooks|
-|[`@redwoodjs/auth`](https://github.com/redwoodjs/redwood/blob/main/packages/auth/README.md#contributing)|A lightweight wrapper around popular SPA authentication libraries|
-|[`@redwoodjs/cli`](https://github.com/redwoodjs/redwood/blob/main/packages/cli/README.md)|All the commands for Redwood's built-in CLI|
-|[`@redwoodjs/codemods`](https://github.com/redwoodjs/redwood/blob/main/packages/codemods/README.md)|Codemods that automate upgrading a Redwood project|
-|[`@redwoodjs/core`](https://github.com/redwoodjs/redwood/blob/main/packages/core/README.md)|Defines babel plugins and config files|
-|[`@redwoodjs/create-redwood-app`](https://github.com/redwoodjs/redwood/blob/main/packages/create-redwood-app/README.md)|Enables `yarn create redwood-app`—downloads the latest release of Redwood and extracts it into the supplied directory|
-|[`@redwoodjs/eslint-config`](https://github.com/redwoodjs/redwood/blob/main/packages/eslint-config/README.md)|Defines Redwood's eslint config|
-|[`@redwoodjs/forms`](https://github.com/redwoodjs/redwood/blob/main/packages/forms/README.md)|Provides Form helpers|
-|[`@redwoodjs/graphql-server`](https://github.com/redwoodjs/redwood/blob/main/packages/graphql-server/README.md)|Exposes functions to build the GraphQL API, provides services with `context`, and a set of envelop plugins to supercharge your GraphQL API with logging, authentication, error handling, directives and more|
-|[`@redwoodjs/internal`](https://github.com/redwoodjs/redwood/blob/main/packages/internal/README.md)|Provides tooling to parse Redwood configs and get a project's paths|
-|[`@redwoodjs/prerender`](https://github.com/redwoodjs/redwood/blob/main/packages/prerender/README.md)|Defines functionality for prerendering static content|
-|[`@redwoodjs/record`](https://github.com/redwoodjs/redwood/blob/main/packages/record/README.md)|ORM built on top of Prisma. It may be extended in the future to wrap other database access packages|
-|[`@redwoodjs/router`](https://github.com/redwoodjs/redwood/blob/main/packages/router/README.md)|The built-in router for Redwood|
-|[`@redwoodjs/structure`](https://github.com/redwoodjs/redwood/blob/main/packages/structure/README.md)|Provides a way to build, validate and inspect an object graph that represents a complete Redwood project|
-|[`@redwoodjs/telemetry`](https://github.com/redwoodjs/redwood/blob/main/packages/telemetry/README.md)|Provides functionality for anonymous data collection|
-|[`@redwoodjs/testing`](https://github.com/redwoodjs/redwood/blob/main/packages/testing/README.md)|Provides helpful defaults when testing a Redwood project's web side|
-|[`@redwoodjs/web`](https://github.com/redwoodjs/redwood/blob/main/packages/web/README.md)|Configures a Redwood's app web side: wraps the Apollo Client in `RedwoodApolloProvider`; defines the Cell HOC|
+
+| Package | Description |
+| :---------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [`@redwoodjs/api-server`](https://github.com/redwoodjs/redwood/blob/main/packages/api-server/README.md) | Run a Redwood app using Fastify server (alternative to serverless API) |
+| [`@redwoodjs/api`](https://github.com/redwoodjs/redwood/blob/main/packages/api/README.md) | Infrastructure components for your applications UI including logging, webhooks, authentication decoders and parsers, as well as tools to test custom serverless functions and webhooks |
+| [`@redwoodjs/auth`](https://github.com/redwoodjs/redwood/blob/main/packages/auth/README.md#contributing) | A lightweight wrapper around popular SPA authentication libraries |
+| [`@redwoodjs/cli`](https://github.com/redwoodjs/redwood/blob/main/packages/cli/README.md) | All the commands for Redwood's built-in CLI |
+| [`@redwoodjs/codemods`](https://github.com/redwoodjs/redwood/blob/main/packages/codemods/README.md) | Codemods that automate upgrading a Redwood project |
+| [`@redwoodjs/core`](https://github.com/redwoodjs/redwood/blob/main/packages/core/README.md) | Defines babel plugins and config files |
+| [`@redwoodjs/create-redwood-app`](https://github.com/redwoodjs/redwood/blob/main/packages/create-redwood-app/README.md) | Enables `yarn create redwood-app`—downloads the latest release of Redwood and extracts it into the supplied directory |
+| [`@redwoodjs/eslint-config`](https://github.com/redwoodjs/redwood/blob/main/packages/eslint-config/README.md) | Defines Redwood's eslint config |
+| [`@redwoodjs/forms`](https://github.com/redwoodjs/redwood/blob/main/packages/forms/README.md) | Provides Form helpers |
+| [`@redwoodjs/graphql-server`](https://github.com/redwoodjs/redwood/blob/main/packages/graphql-server/README.md) | Exposes functions to build the GraphQL API, provides services with `context`, and a set of envelop plugins to supercharge your GraphQL API with logging, authentication, error handling, directives and more |
+| [`@redwoodjs/internal`](https://github.com/redwoodjs/redwood/blob/main/packages/internal/README.md) | Provides tooling to parse Redwood configs and get a project's paths |
+| [`@redwoodjs/prerender`](https://github.com/redwoodjs/redwood/blob/main/packages/prerender/README.md) | Defines functionality for prerendering static content |
+| [`@redwoodjs/record`](https://github.com/redwoodjs/redwood/blob/main/packages/record/README.md) | ORM built on top of Prisma. It may be extended in the future to wrap other database access packages |
+| [`@redwoodjs/router`](https://github.com/redwoodjs/redwood/blob/main/packages/router/README.md) | The built-in router for Redwood |
+| [`@redwoodjs/structure`](https://github.com/redwoodjs/redwood/blob/main/packages/structure/README.md) | Provides a way to build, validate and inspect an object graph that represents a complete Redwood project |
+| [`@redwoodjs/telemetry`](https://github.com/redwoodjs/redwood/blob/main/packages/telemetry/README.md) | Provides functionality for anonymous data collection |
+| [`@redwoodjs/testing`](https://github.com/redwoodjs/redwood/blob/main/packages/testing/README.md) | Provides helpful defaults when testing a Redwood project's web side |
+| [`@redwoodjs/web`](https://github.com/redwoodjs/redwood/blob/main/packages/web/README.md) | Configures a Redwood's app web side: wraps the Apollo Client in `RedwoodApolloProvider`; defines the Cell HOC |
## Contributing Docs
@@ -146,12 +152,15 @@ These docs are in the Framework repo, redwoodjs/redwood, and explain how to cont
In general, they should consist of more straightforward explanations, are allowed to be technically heavy, and should be written for a more experienced audience. But as a best practice for collaborative projects, they should still provide a Vision + Roadmap and identify the project-point person(s) (or lead(s)).
## What makes for a good Pull Request?
+
In general, we don’t have a formal structure for PRs. Our goal is to make it as efficient as possible for anyone to open a PR. But there are some good practices, which are flexible. Just keep in mind that after opening a PR there’s more to do before getting to the finish line:
+
1. Reviews from other contributors and maintainers
2. Update code and, after maintainer approval, merge-in changes to the `main` branch
3. Once PR is merged, it will be released and added to the next version Release Notes with a link for anyone to look at the PR and understand it.
Some tips and advice:
+
- **Connect the dots and leave a breadcrumb**: link to related Issues, Forum discussions, etc. Help others follow the trail leading up to this PR.
- **A Helpful Description**: What does the code in the PR do and what problem does it solve? How can someone use the code? Code sample, Screenshot, Quick Video… Any or all of this is so so good.
- **Draft or Work in Progress**: You don’t have to finish the code to open a PR. Once you have a start, open it up! Most often the best way to move an Issue forward is to see the code in action. Also, often this helps identify ways forward before you spend a lot of time polishing.
@@ -162,7 +171,9 @@ Some tips and advice:
The best thing you can do is look through existing PRs, which will give you a feel for how things work and what you think is helpful.
### Example PR
+
If you’re looking for an example of “what makes a good PR”, look no further than this one by Kim-Adeline:
+
- [Convert component generator to TS #632](https://github.com/redwoodjs/redwood/pull/632)
Not every PR needs this much information. But it’s definitely helpful when it does!
diff --git a/docs/docs/contributing-walkthrough.md b/docs/docs/contributing-walkthrough.md
index 89560c3907db..5747b425e799 100644
--- a/docs/docs/contributing-walkthrough.md
+++ b/docs/docs/contributing-walkthrough.md
@@ -14,11 +14,11 @@ description: Watch a video of the contributing process
> 3. 🪜 **Step-by-step Walkthrough** (👈 you are here)
> 4. 📈 [Current Project Status: v1 Release Board](https://github.com/orgs/redwoodjs/projects/6)
> 5. 🤔 What should I work on?
-> - ["Help Wanted" v1 Triage Board](https://redwoodjs.com/good-first-issue)
-> - [Discovery Process and Open Issues](contributing-overview.md#what-should-i-work-on)
-
+> - ["Help Wanted" v1 Triage Board](https://redwoodjs.com/good-first-issue)
+> - [Discovery Process and Open Issues](contributing-overview.md#what-should-i-work-on)
## Video Recording of Complete Contributing Process
+
The following recording is from a Contributing Workshop, following through the exact steps outlined below. The Workshop includes additional topics along with Q&A discussion.
## Prologue: Getting Started with Redwood and GitHub (and git)
+
These are the foundations for contributing, which you should be familiar with before starting the walkthrough.
[**The Redwood Tutorial**](tutorial/foreword.md)
@@ -42,6 +43,7 @@ The best (and most fun) way to learn Redwood and the underlying tools and techno
- And browse through [How To's](how-to/index)
### GitHub (and Git)
+
Diving into Git and the GitHub workflow can feel intimidating if you haven’t experienced it before. The good news is there’s a lot of great material to help you learn and be committing in no time!
- [Introduction to GitHub](https://lab.github.com/githubtraining/introduction-to-github) (overview of concepts and workflow)
@@ -51,7 +53,9 @@ Diving into Git and the GitHub workflow can feel intimidating if you haven’t e
## The Full Workflow: From Local Development to a New PR
### Definitions
+
#### Redwood “Project”
+
We refer to the codebase of a Redwood application as a Project. This is what you install when you run `yarn create redwood-app `. It’s the thing you are building with Redwood.
Lastly, you’ll find the template used to create a new project (when you run create redwood-app) here in GitHub: [redwoodjs/redwood/packages/create-redwood-app/template/](https://github.com/redwoodjs/redwood/tree/main/packages/create-redwood-app/template)
@@ -59,9 +63,11 @@ Lastly, you’ll find the template used to create a new project (when you run cr
We refer to this as the **CRWA Template or Project Template**.
#### Redwood “Framework”
+
The Framework is the codebase containing all the packages (and other code) that is published on NPMjs.com as `@redwoodjs/`. The Framework repository on GitHub is here: [https://github.com/redwoodjs/redwood](https://github.com/redwoodjs/redwood)
### Development tools
+
These are the tools used and recommended by the Core Team.
**VS Code**
@@ -80,6 +86,7 @@ There’s nothing wrong with Terminal (on Mac) and plain zsh or bash. (If you’
Unfortunately, there are a lot of “gotchas” when it comes to working with Javascript-based frameworks on Windows. We do our best to point out (and resolve) issues, but our priority focus is on developing a Redwood app vs contributing to the Framework. (If you’re interested, there’s a lengthy Forum conversation about this with many suggestions.)
All that said, we highly recommend using one of the following setups to maximize your workflow:
+
1. Use [Git for Windows and Git Bash](how-to/windows-development-setup.md) (included in installation)
2. Use [WSL following this setup guide on the Forums](https://community.redwoodjs.com/t/windows-subsystem-for-linux-setup/2439)
@@ -93,17 +100,21 @@ But don’t skip out reading the following steps in “Local Development Setup
But when you’re ready, learn how to use it in the section at the end [“GitPod: Browser-based Development”](#gitpod-browser-based-development).
### Local Development Setup
+
#### Step 1: Redwood Framework
+
1. **Fork the [Redwood Framework](https://github.com/redwoodjs/redwood)** into a personal repo
2. Using GitHub Desktop, **open the Framework Codebase** in a VS Code workspace
3. Commands to “**start fresh**” when working on the Framework
- - `yarn install`: This installs the package dependencies in /node_modules using Yarn package manager. This command is the same as just typing `yarn`. Also, if you ever switch branches and want to make sure the install dependencies are correct, you can run `yarn install --force` (shorthand `yarn -f`).
- - `git clean -fxd`: *You’ll only need to do this if you’ve already been developing and want to “start over” and reset your codebase*. This command will permanently delete everything that is .gitignored, e.g. /node_modules and /dist directories with package builds. When switching between branches, this command makes sure nothing is carried over that you don’t want. (Warning: it will delete .env files in a Redwood Project. To avoid this, you can use `git clean -fxd -e .env`.)
+ - `yarn install`: This installs the package dependencies in /node_modules using Yarn package manager. This command is the same as just typing `yarn`. Also, if you ever switch branches and want to make sure the install dependencies are correct, you can run `yarn install --force` (shorthand `yarn -f`).
+ - `git clean -fxd`: _You’ll only need to do this if you’ve already been developing and want to “start over” and reset your codebase_. This command will permanently delete everything that is .gitignored, e.g. /node_modules and /dist directories with package builds. When switching between branches, this command makes sure nothing is carried over that you don’t want. (Warning: it will delete .env files in a Redwood Project. To avoid this, you can use `git clean -fxd -e .env`.)
4. **Create a new branch** from the `main` branch
-First make sure you’ve pulled all changes from the remote origin (GitHub repo) into your local branch. (If you just cloned from your fork, you should be up to date.) Then create a new branch. The nomenclature used by David Price is `-description-with-hyphens`, e.g. `dsp-add-eslint-config-redwood-toml`. It's simple to use VS Code or GitHub Desktop to manage branches. You can also do this via the CLI git checkout command.
+ First make sure you’ve pulled all changes from the remote origin (GitHub repo) into your local branch. (If you just cloned from your fork, you should be up to date.) Then create a new branch. The nomenclature used by David Price is `-description-with-hyphens`, e.g. `dsp-add-eslint-config-redwood-toml`. It's simple to use VS Code or GitHub Desktop to manage branches. You can also do this via the CLI git checkout command.
#### Step 2: Test Project
+
There are several options for creating a local Redwood Project to use during development. Anytime you are developing against a test project, there are some specific gotchas to keep in mind:
+
- New projects always use the latest stable version of the Redwood packages, which will not be up to date with the latest Framework code in the `main` branch.
- To use the packages corresponding with the latest code in the Framework `main` branch, you can use the canary version published to NPM. All you need to do to install the canary versions is run `yarn rw upgrade --tag canary` in your Project
- Using a cloned project or repo? Just know there are likely breaking changes in `main` that haven’t been applied. You can examine merged PRs with the “breaking” label for more info.
@@ -112,44 +123,49 @@ There are several options for creating a local Redwood Project to use during dev
With those details out of the way, now is the time to choose an option below that meets your needs based on functionality and codebase version.
**Build a Functional Test Project [Recommended]**
+
1. 👉 **Use the build script to create a test project**: From the Framework root directory, run `yarn build:test-project `. This command installs a new project using the Template codebase from your current Framework branch, it then adds Tutorial features, and finally it initializes the DB (with seed data!). It should work 90% of the time and is the recommended starting place. We also use this out-of-the-box with Gitpod.
**Other Options to create a project**
2. **Install a fresh project using the local Framework template code:** Sometimes you need to create a project that uses the Template codebase in your local branch of the Framework, e.g. your changes include modifications to the CRWA Template and need to be tested. Running the command above is exactly the same as `yarn create redwood- app …`, only it runs the command from your local Framework package using the local Template codebase. Note: this is the same command used at the start of the `yarn build:test-project` command.
+
```
yarn babel-node packages/create-redwood-app/src/create-redwood-app.js
```
3. **Clone the Redwood Tutorial App repo:** This is the codebase to use when starting the Redwood Tutorial Part 2. It is updated to the latest version and has the Blog features. This is often something we use for local development. Note: be sure to upgrade to canary and look out for breaking changes coming with the next release.
-
4. **Install a fresh project**: `yarn create redwood-app ` If you just need a fresh installation 1) using the latest version template codebase and 2) without any features, then just install a new Redwood project. Note: this can have the same issues regarding the need to upgrade to canary and addressing breaking changes (see Notes from items 2 and 3 above).
> Note: All the options above currently set the language to JavaScript. If you would like to work with TypeScript, you can add the option `--typescript` to either of the commands that run the create-redwood-app installation.
#### Step 3: Link the local Framework with the local test Project
+
Once you work on the Framework code, you’ll most often want to run the code in a Redwood app for testing. However, the Redwood Project you created for testing is currently using the latest version (or canary) packages of Redwood published on NPMjs.com, e.g. [@redwoodjs/core](https://www.npmjs.com/package/@redwoodjs/core)
So we’ll use the Redwood Framework (rwfw) command to connect our local Framework and test Projects, which allows the Project to run on the code for Packages we are currently developing.
Run this command from the CLI in your test Project:
+
```
RWFW_PATH= yarn rwfw project:sync
```
For Example:
+
```
cd redwood-project
RWFW_PATH=~/redwood yarn rwfw project:sync
```
-RWFW_PATH is the path to your local copy of the Redwood Framework. _Once provided to rwfw, it'll remember it and you shouldn't have to provide it again unless you move it._
+RWFW*PATH is the path to your local copy of the Redwood Framework. \_Once provided to rwfw, it'll remember it and you shouldn't have to provide it again unless you move it.*
> **Heads up for Windows Devs**
> Depending on your dev setup, Windows might balk at you setting the env var RWFW_PATH at the beginning of the command like this. If so, try prepending with `cross-env`, e.g. `yarn cross-env RWFW_PATH=~/redwood yarn rwfw` ... Or you can add the env var and value directly to your shell before running the command.
As project:sync starts up, it'll start logging to the console. In order, it:
+
1. cleans and builds the framework
2. copies the framework's dependencies to your project
3. runs yarn install in your project
@@ -161,11 +177,13 @@ Step two is the only explicit change you'll see to your project. You'll see that
All done? You’re ready to kill the link process with “ctrl + c”. You’ll need to confirm your root package.json no longer has the added dependencies. And, if you want to reset your test-project, you should run `yarn install --force`.
#### Step 4: Framework Package(s) Local Testing
+
Within your Framework directory, use the following tools and commands to test your code:
+
1. **Build the packages**: `yarn build`
- - to delete all previous build directories: yarn build:clean
+ - to delete all previous build directories: yarn build:clean
2. **Syntax and Formatting**: `yarn lint`
- - to fix errors or warnings: `yarn lint:fix`
+ - to fix errors or warnings: `yarn lint:fix`
3. **Run unit tests for each package**: `yarn test`
4. **Run through the Cypress E2E integration tests**: `yarn e2e`
5. **Check Yarn resolutions and package.json format**: `yarn check`
@@ -173,14 +191,17 @@ Within your Framework directory, use the following tools and commands to test yo
All of these checks are included in Redwood’s GitHub PR Continuous Integration (CI) automation. However, it’s good practice to understand what they do by using them locally. The E2E tests aren’t something we use every time anymore (because it takes a while), but you should learn how to use it because it comes in handy when your code is failing tests on GitHub and you need to diagnose.
> **Heads up for Windows Devs**
-> The Cypress E2E does *not* work on Windows. Two options are available if needed:
+> The Cypress E2E does _not_ work on Windows. Two options are available if needed:
+>
> 1. Use Gitpod (see related section for info)
> 2. When you create a PR, just ask for help from a maintainer
#### Step 5: Open a PR 🚀
+
You’ve made it to the fun part! It’s time to use the code you’re working on to create a new PR into the Redwood Framework `main` branch.
We use GitHub Desktop to walk through the process of:
+
- Committing my changes to my development branch
- Publishing (pushing) my branch and changes to my GitHub repo fork of the Redwood Framework
- Opening a PR requesting to merge my forked-repo branch into the Redwood Framework `main` branch
@@ -200,7 +221,7 @@ You have successfully submitted your PR!
**Note:** Make sure you check the box that allows project maintainers to update your branch. This option is found on the "Open a pull request" form below the description textbox. Checking this option helps move a PR forward more quickly, as branches always need to be updated from `main` before we can merge.
**When is my code “ready” to open a PR?**
-Most of the action, communication, and decisions happen within a PR. A common mistake new contributors make is *waiting* until their code is “perfect” before opening a PR. Assuming your PR has some code changes, it’s great practice to open a [Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/) (setting during the PR creation), which you can use to start discussion and ask questions. PRs are closed all the time without being merged, often because they are replaced by another PR resulting from decisions and discussion. It’s part of the process. More importantly, it means collaboration is happening!
+Most of the action, communication, and decisions happen within a PR. A common mistake new contributors make is _waiting_ until their code is “perfect” before opening a PR. Assuming your PR has some code changes, it’s great practice to open a [Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/) (setting during the PR creation), which you can use to start discussion and ask questions. PRs are closed all the time without being merged, often because they are replaced by another PR resulting from decisions and discussion. It’s part of the process. More importantly, it means collaboration is happening!
What isn’t a fun experience is spending a whole bunch of time on code that ends up not being the correct direction or is unnecessary/redundant to something that already exists. This is a part of the learning process. But it’s another reason to open a draft PR sooner than later to get confirmation and questions out of the way before investing time into refining and details.
@@ -209,7 +230,9 @@ When in doubt, just try first and ask for help and direction!
Refer to the [What makes for a good Pull Request?](contributing-overview.md#what-makes-for-a-good-pull-request) section in [Contributing Overview](contributing-overview.md)for general good practices when opening PR.
### Gitpod: Browser-based Development
+
[Gitpod](http://gitpod.io) has recently been integrated with Redwood to JustWork™ with any branch or PR. When a virtual Gitpod workspace is initialized, it automatically:
+
1. Checks-out the code from your branch or PR
2. Run Yarn installation
3. Creates the functional Test Project via `yarn build:test-project`
@@ -222,6 +245,7 @@ Refer to the [What makes for a good Pull Request?](contributing-overview.md#what
**Demo of Gitpod**
David briefly walks-through an automatically prebuilt Gitpod workspace here:
+
- [Gitpod + RedwoodJS 3-minute Walkthrough](https://youtu.be/_kMuTW3x--s)
Make sure you watch until the end where David shows how to set up your integration with GitHub and VS Code sync. 🤩
@@ -229,12 +253,12 @@ Make sure you watch until the end where David shows how to set up your integrati
**Start a Gitpod Workspace**
There are two ways to get started with Gitpod + Redwood.
-*Option 1: Open a PR*
+_Option 1: Open a PR_
Every PR will trigger a Gitpod prebuild using the PR branch. Just look for Gitpod in the list of checks at the bottom of the PR — click the “Details” link and away you’ll go!
-*Option 2: Use the link from your project or branch*
+_Option 2: Use the link from your project or branch_
You can initialize a workspace using this URL pattern:
@@ -243,9 +267,9 @@ https://gitpod.io/#
```
For example, this link will start a workspace using the RedwoodJS main branch:
+
- https://gitpod.io/#https://github.com/redwoodjs/redwood
And this link will start a workspace for a PR #3434:
-- https://gitpod.io/#https://github.com/redwoodjs/redwood/pull/3434
-
+- https://gitpod.io/#https://github.com/redwoodjs/redwood/pull/3434
diff --git a/docs/docs/cors.md b/docs/docs/cors.md
index 5325cedc2ba0..3781aeb19fe2 100644
--- a/docs/docs/cors.md
+++ b/docs/docs/cors.md
@@ -19,7 +19,7 @@ This will become obvious when you point your browser to your site and see none o
Dealing with CORS can complicate your app and make it harder to deploy to new hosts, run in different environments, etc. Is there a way to avoid CORS altogether?
-Yes! If you can add a proxy between your web and api sides, all requests will *appear* to be going to and from the same domain (the web side, even though behind the scenes they are forwarded somewhere else). This functionality is included automatically with hosts like [Netlify](https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service) or [Vercel](https://vercel.com/docs/cli#project-configuration/rewrites). With a host like [Render](https://render-web.onrender.com/docs/deploy-redwood#deployment) you can enable a proxy with a simple config option. Most providers should provide this functionality through a combination of provider-specific config and/or web server configuration.
+Yes! If you can add a proxy between your web and api sides, all requests will _appear_ to be going to and from the same domain (the web side, even though behind the scenes they are forwarded somewhere else). This functionality is included automatically with hosts like [Netlify](https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service) or [Vercel](https://vercel.com/docs/cli#project-configuration/rewrites). With a host like [Render](https://render-web.onrender.com/docs/deploy-redwood#deployment) you can enable a proxy with a simple config option. Most providers should provide this functionality through a combination of provider-specific config and/or web server configuration.
## GraphQL Config
@@ -40,7 +40,7 @@ export const handler = createGraphQLHandler({
})
```
-Note that the `origin` needs to be a complete URL including the scheme (`https`). This is the domain that requests are allowed to come *from*. In this example we assume the web side is served from `https://www.example.com`. If you have multiple servers that should be allowed to access the api, you can pass an array of them instead:
+Note that the `origin` needs to be a complete URL including the scheme (`https`). This is the domain that requests are allowed to come _from_. In this example we assume the web side is served from `https://www.example.com`. If you have multiple servers that should be allowed to access the api, you can pass an array of them instead:
```jsx
cors: {
@@ -56,11 +56,11 @@ The following config only applies if you're using [dbAuth](authentication.md#sel
You'll need to configure several things:
-* Add CORS config for GraphQL
-* Add CORS config for the auth function
-* Cookie config for the auth function
-* Allow sending of credentials in GraphQL XHR requests
-* Allow sending of credentials in auth function requests
+- Add CORS config for GraphQL
+- Add CORS config for the auth function
+- Cookie config for the auth function
+- Allow sending of credentials in GraphQL XHR requests
+- Allow sending of credentials in auth function requests
Here's how you configure each of these:
@@ -84,7 +84,7 @@ export const handler = createGraphQLHandler({
})
```
-`origin` is the domain(s) that requests come *from* (the web side).
+`origin` is the domain(s) that requests come _from_ (the web side).
### Auth CORS Config
@@ -119,7 +119,7 @@ const authHandler = new DbAuthHandler(event, context, {
})
```
-Just like the GraphQL config, `origin` is the domain(s) that requests come *from* (the web side).
+Just like the GraphQL config, `origin` is the domain(s) that requests come _from_ (the web side).
### Cookie Config
@@ -209,7 +209,7 @@ Forwarding http://fb6d701c44b5.ngrok.io -> http://localhost:8911
Forwarding https://fb6d701c44b5.ngrok.io -> http://localhost:8911
```
-Note the two different domains. Copy the `https` domain from the api side because we'll need it in a moment. Even if the Redwood dev server isn't running you can leave these tunnels running, and when the dev server *does* start, they'll just start on those domains again.
+Note the two different domains. Copy the `https` domain from the api side because we'll need it in a moment. Even if the Redwood dev server isn't running you can leave these tunnels running, and when the dev server _does_ start, they'll just start on those domains again.
### `redwood.toml` Config
@@ -255,7 +255,7 @@ Where you get this domain from will depend on how you expose your app to the out
You'll need to apply an option when starting the dev server to tell it to accept requests from any host, not just `localhost`:
```bash
-> yarn rw dev --fwd="--allowed-hosts all"
+rw > yarn dev --fwd="--allowed-hosts all"
```
### Wrapping Up
diff --git a/docs/docs/create-redwood-app.md b/docs/docs/create-redwood-app.md
index 61e2d15b5dfe..1d4fe329bde7 100644
--- a/docs/docs/create-redwood-app.md
+++ b/docs/docs/create-redwood-app.md
@@ -12,6 +12,7 @@ yarn create redwood-app
```
## Set up for success
+
Redwood requires that you're running Node version 20 or higher.
If you're running Node version 21.0.0 or higher, you can still use Create Redwood App, but it may make your project incompatible with some deploy targets, such as AWS Lambdas.
@@ -35,11 +36,13 @@ To upgrade your version of yarn, [you can refer to the yarn documentation](https
## What you can expect
### Select your preferred language
+
Options: TypeScript (default) or JavaScript
If you choose JavaScript, you can always [add TypeScript later](/docs/typescript/introduction#converting-a-javascript-project-to-typescript).
### Do you want to initialize a git repo?
+
Options: yes (default) or no
If you mark "yes", then it will ask you to **Enter a commit message**. The default message is "Initial commit."
@@ -56,6 +59,7 @@ git commit -m "Initial commit"
If you're new to git, here's a recommended playlist on YouTube: [git for Beginners](https://www.youtube.com/playlist?list=PLrz61zkUHJJFmfTgOVL1mBw_NZcgGe882)
### Do you want to run `yarn install`?
+
Options: yes (default) or no
_NOTE: This prompt will only display if you're running yarn, version 1._
@@ -83,21 +87,20 @@ yarn rw dev
- You can visit the Redwood GraphQL Playground at `http://localhost:8911/graphql`.
## Flags
+
You can by pass these prompts by using the following flags:
-| Flag | Alias | What it does |
-| :--- | :--- | :--- |
-| `--yarn-install` | | Run `yarn install` |
-| `--typescript` | `ts` | Set TypeScript as the preferred language (pass `--no-typescript` to use JavaScript) |
-| `--overwrite` | | Overwrites the existing directory, if it has the same name |
-| `--git-init` | `git` | Initializes a git repository |
-| `--commit-message "Initial commit"` | `m` | Specifies the initial git commit message |
-| `--yes` | `y` | Automatically select all defaults |
+| Flag | Alias | What it does |
+| :---------------------------------- | :---- | :---------------------------------------------------------------------------------- |
+| `--yarn-install` | | Run `yarn install` |
+| `--typescript` | `ts` | Set TypeScript as the preferred language (pass `--no-typescript` to use JavaScript) |
+| `--overwrite` | | Overwrites the existing directory, if it has the same name |
+| `--git-init` | `git` | Initializes a git repository |
+| `--commit-message "Initial commit"` | `m` | Specifies the initial git commit message |
+| `--yes` | `y` | Automatically select all defaults |
For example, here's the project with all flags enabled:
```terminal
yarn create redwood-app --typescript --git-init --commit-message "Initial commit" --yarn-install
```
-
-
diff --git a/docs/docs/data-migrations.md b/docs/docs/data-migrations.md
index 3f7d32d389fd..4f7e85e03e94 100644
--- a/docs/docs/data-migrations.md
+++ b/docs/docs/data-migrations.md
@@ -8,12 +8,12 @@ description: Track changes to database content
There are two kinds of changes you can make to your database:
-* Changes to structure
-* Changes to content
+- Changes to structure
+- Changes to content
-In Redwood, [Prisma Migrate](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-migrate) takes care of codifying changes to your database *structure* in code by creating a snapshot of changes to your database that can be reliably repeated to end up in some known state.
+In Redwood, [Prisma Migrate](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-migrate) takes care of codifying changes to your database _structure_ in code by creating a snapshot of changes to your database that can be reliably repeated to end up in some known state.
-To track changes to your database *content*, Redwood includes a feature we call **Data Migration**. As your app evolves and you move data around, you need a way to consistently declare how that data should move.
+To track changes to your database _content_, Redwood includes a feature we call **Data Migration**. As your app evolves and you move data around, you need a way to consistently declare how that data should move.
Imagine a `User` model that contains several columns for user preferences. Over time, you may end up with more and more preferences to the point that you have more preference-related columns in the table than you do data unique to the user! This is a common occurrence as applications grow. You decide that the app should have a new model, `Preference`, to keep track of them all (and `Preference` will have a foreign key `userId` to reference it back to its `User`). You'll use Prisma Migrate to create the new `Preference` model, but how do you copy the preference data to the new table? Data migrations to the rescue!
@@ -22,9 +22,11 @@ Imagine a `User` model that contains several columns for user preferences. Over
Just like Prisma, we will store which data migrations have run in the database itself. We'll create a new database table `DataMigration` to keep track of which ones have run already.
Rather than create this model by hand, Redwood includes a CLI tool to add the model to `schema.prisma` and create the DB migration that adds the table to the database:
+
```
yarn rw data-migrate install
```
+
You'll see a new directory created at `api/db/dataMigrations` which will store our individual migration tasks.
Take a look at `schema.prisma` to see the new model definition:
@@ -39,21 +41,27 @@ model RW_DataMigration {
```
The install script also ran `yarn rw prisma migrate dev --create-only` automatically so you have a DB migration ready to go. You just need to run the `prisma migrate dev` command to apply it:
+
```
yarn rw prisma migrate dev
```
+
## Creating a New Data Migration
Data migrations are just plain Typescript or Javascript files which export a single anonymous function that is given a single argument—an instance of `PrismaClient` called `db` that you can use to access your database. The files have a simple naming convention:
+
```
{version}-{name}.js
```
+
Where `version` is a timestamp, like `20200721123456` (an ISO8601 datetime without any special characters or zone identifier), and `name` is a param-case human readable name for the migration, like `copy-preferences`.
To create a data migration we have a generator:
+
```
yarn rw generate dataMigration copyPreferences
```
+
This will create `api/db/dataMigrations/20200721123456-copy-preferences.js`:
```jsx title="api/db/dataMigrations/20200721123456-copy-preferences.js"
@@ -84,8 +92,8 @@ export default async ({ db }) => {
newsletter: user.newsletter,
frequency: user.frequency,
theme: user.theme,
- user: { connect: { id: user.id } }
- }
+ user: { connect: { id: user.id } },
+ },
})
})
}
@@ -100,23 +108,25 @@ This loops through each existing `User` and creates a new `Preference` record co
>
> When going to production, you would need to run this as two separate deploys to ensure no data is lost.
>
-> The reason is that all DB migrations are run and *then* all data migrations. So if you had two DB migrations (one to create `Preference` and one to drop the unneeded columns from `User`) they would both run before the Data Migration, so the columns containing the preferences are gone before the data migration gets a chance to copy them over!
+> The reason is that all DB migrations are run and _then_ all data migrations. So if you had two DB migrations (one to create `Preference` and one to drop the unneeded columns from `User`) they would both run before the Data Migration, so the columns containing the preferences are gone before the data migration gets a chance to copy them over!
>
> **Remember**: Any destructive action on the database (removing a table or column especially) needs to be a two step process to avoid data loss.
## Running a Data Migration
When you're ready, you can execute your data migration with `data-migrate`'s `up` command:
+
```
yarn rw data-migrate up
```
+
This goes through each file in `api/db/dataMigrations`, compares it against the list of migrations that have already run according to the `DataMigration` table in the database, and executes any that aren't present in that table, sorted oldest to newest based on the timestamp in the filename.
Any logging statements (like `console.info()`) you include in your data migration script will be output to the console as the script is running.
If the script encounters an error, the process will abort, skipping any following data migrations.
-> The example data migration above didn't include this for brevity, but you should always run your data migration [inside a transaction](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/transactions#bulk-operations-experimental) so that if any errors occur during execution the database will not be left in an inconsistent state where only *some* of your changes were performed.
+> The example data migration above didn't include this for brevity, but you should always run your data migration [inside a transaction](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/transactions#bulk-operations-experimental) so that if any errors occur during execution the database will not be left in an inconsistent state where only _some_ of your changes were performed.
## Long-term Maintainability
@@ -137,8 +147,8 @@ export default async ({ db }) => {
newsletter: user.newsletter,
frequency: user.frequency,
theme: user.theme,
- user: { connect: { id: user.id } }
- }
+ user: { connect: { id: user.id } },
+ },
})
})
}
@@ -148,11 +158,14 @@ export default async ({ db }) => {
## Lifecycle Summary
Run once:
+
```
yarn rw data-migrate install
yarn rw prisma migrate dev
```
+
Run every time you need a new data migration:
+
```
yarn rw generate dataMigration migrationName
yarn rw data-migrate up
diff --git a/docs/docs/database-seeds.md b/docs/docs/database-seeds.md
index 65ffe013993a..e8cdc1917ce5 100644
--- a/docs/docs/database-seeds.md
+++ b/docs/docs/database-seeds.md
@@ -7,15 +7,15 @@ a new environment.
Seed data are things like:
-* An admin user so that you can log in to your new instance
-* A list of categories that can be assigned to a Product
-* Lists of roles and permissions
+- An admin user so that you can log in to your new instance
+- A list of categories that can be assigned to a Product
+- Lists of roles and permissions
Seed data is not meant for:
-* Sample data to be used in development
-* Data to run tests against
-* Randomized data
+- Sample data to be used in development
+- Data to run tests against
+- Randomized data
## Best Practices
@@ -31,7 +31,7 @@ already exists, and if so just update it, if not then create it. But, this
technique requires a separate SQL statement for each member of your data array
and is less performant than `createMany()`.
-You could also do a check if *any* data exists in the database first, and if
+You could also do a check if _any_ data exists in the database first, and if
not, create the records with `createMany()`. However, this means that any
existing seed data that may have been modified will remain, and would not be
updated to match what you expect in your seed.
@@ -46,7 +46,7 @@ Seeds are automatically run the first time you migrate your database:
yarn rw prisma migrate dev
```
-They are run *every* time you reset your database:
+They are run _every_ time you reset your database:
```bash
yarn rw prisma migrate reset
@@ -148,14 +148,14 @@ export default async () => {
{ name: 'Fiction', bisacCode: 'FIC000000' },
{ name: 'Nature', bisacCode: 'NAT000000' },
{ name: 'Travel', bisacCode: 'TRV000000' },
- { name: 'World History', bisacCode: 'HIS037000' }
+ { name: 'World History', bisacCode: 'HIS037000' },
]
for (const item of data) {
- await db.category.upsert({
+ await db.category.upsert({
where: { name: item.name },
update: { code: item.code },
- create: { name: item.name, code: item.code }
+ create: { name: item.name, code: item.code },
})
}
} catch (error) {
@@ -167,7 +167,7 @@ export default async () => {
You can now execute this seed as many times as you want and you'll end up with
that exact list in the database each time. And, any additional categories you've
created in the meantime will remain. Remember: seeds are meant to be the
-*minimum* amount of data you need for your app to run, not necessarily *all* the
+_minimum_ amount of data you need for your app to run, not necessarily _all_ the
data that will ever be present in those tables.
# Seeding users for dbAuth
@@ -182,27 +182,27 @@ import { hashPassword } from '@redwoodjs/auth-dbauth-api'
export default async () => {
const users = [
{ name: 'John', email: 'john@example.com', password: 'secret1' },
- { name: 'Jane', email: 'jane@example.com', password: 'secret2' }
+ { name: 'Jane', email: 'jane@example.com', password: 'secret2' },
]
for (const user of users) {
const [hashedPassword, salt] = hashPassword(user.password)
await db.user.upsert({
- where: {
- email: user.email
+ where: {
+ email: user.email,
},
- create: {
+ create: {
name: user.name,
email: user.email,
hashedPassword,
- salt
+ salt,
},
update: {
name: user.name,
hashedPassword,
- salt
- }
+ salt,
+ },
})
}
}
diff --git a/docs/docs/deploy/baremetal.md b/docs/docs/deploy/baremetal.md
index aec882f28381..ba99355f6fa3 100644
--- a/docs/docs/deploy/baremetal.md
+++ b/docs/docs/deploy/baremetal.md
@@ -48,8 +48,9 @@ The Baremetal deploy runs several commands in sequence. These can be customized,
### First Run Lifecycle
If the `--first-run` flag is specified then step 6 above will execute the following commands instead:
- - `pm2 start [service]` - starts the serving process(es)
- - `pm2 save` - saves the running services to the deploy users config file for future startup. See [Starting on Reboot](#starting-on-reboot) for further information
+
+- `pm2 start [service]` - starts the serving process(es)
+- `pm2 save` - saves the running services to the deploy users config file for future startup. See [Starting on Reboot](#starting-on-reboot) for further information
## Directory Structure
@@ -155,29 +156,29 @@ This lists a single server, in the `production` environment, providing the hostn
#### Config Options
-* `host` - hostname to the server
-* `port` - [optional] ssh port for server connection, defaults to 22
-* `username` - the user to login as
-* `password` - [optional] if you are using password authentication, include that here
-* `privateKey` - [optional] if you connect with a private key, include the content of the key here, as a buffer: `privateKey: Buffer.from('...')`. Use this *or* `privateKeyPath`, not both.
-* `privateKeyPath` - [optional] if you connect with a private key, include the path to the key here: `privateKeyPath: path.join('path','to','key.pem')` Use this *or* `privateKey`, not both.
-* `passphrase` - [optional] if your private key contains a passphrase, enter it here
-* `agentForward` - [optional] if you have [agent forwarding](https://docs.github.com/en/developers/overview/using-ssh-agent-forwarding) enabled, set this to `true` and your own credentials will be used for further SSH connections from the server (like when connecting to GitHub)
-* `sides` - An array of sides that will be built on this server
-* `packageManagerCommand` - The package manager bin to call, defaults to `yarn` but could be updated to be prefixed with another command first, for example: `doppler run -- yarn`
-* `monitorCommand` - The monitor bin to call, defaults to `pm2` but could be updated to be prefixed with another command first, for example: `doppler run -- pm2`
-* `path` - The absolute path to the root of the application on the server
-* `migrate` - [optional] Whether or not to run migration processes on this server, defaults to `true`
-* `processNames` - An array of service names from `ecosystem.config.js` which will be (re)started on a successful deploy
-* `repo` - The path to the git repo to clone
-* `branch` - [optional] The branch to deploy (defaults to `main`)
-* `keepReleases` - [optional] The number of previous releases to keep on the server, including the one currently being served (defaults to 5)
+- `host` - hostname to the server
+- `port` - [optional] ssh port for server connection, defaults to 22
+- `username` - the user to login as
+- `password` - [optional] if you are using password authentication, include that here
+- `privateKey` - [optional] if you connect with a private key, include the content of the key here, as a buffer: `privateKey: Buffer.from('...')`. Use this _or_ `privateKeyPath`, not both.
+- `privateKeyPath` - [optional] if you connect with a private key, include the path to the key here: `privateKeyPath: path.join('path','to','key.pem')` Use this _or_ `privateKey`, not both.
+- `passphrase` - [optional] if your private key contains a passphrase, enter it here
+- `agentForward` - [optional] if you have [agent forwarding](https://docs.github.com/en/developers/overview/using-ssh-agent-forwarding) enabled, set this to `true` and your own credentials will be used for further SSH connections from the server (like when connecting to GitHub)
+- `sides` - An array of sides that will be built on this server
+- `packageManagerCommand` - The package manager bin to call, defaults to `yarn` but could be updated to be prefixed with another command first, for example: `doppler run -- yarn`
+- `monitorCommand` - The monitor bin to call, defaults to `pm2` but could be updated to be prefixed with another command first, for example: `doppler run -- pm2`
+- `path` - The absolute path to the root of the application on the server
+- `migrate` - [optional] Whether or not to run migration processes on this server, defaults to `true`
+- `processNames` - An array of service names from `ecosystem.config.js` which will be (re)started on a successful deploy
+- `repo` - The path to the git repo to clone
+- `branch` - [optional] The branch to deploy (defaults to `main`)
+- `keepReleases` - [optional] The number of previous releases to keep on the server, including the one currently being served (defaults to 5)
The easiest connection method is generally to include your own public key in the server's `~/.ssh/authorized_keys` mannually or by running `ssh-copy-id user@server.com` from your local machine, [enable agent forwarding](https://docs.github.com/en/developers/overview/using-ssh-agent-forwarding), and then set `agentForward = true` in `deploy.toml`. This will allow you to use your own credentials when pulling code from GitHub (required for private repos). Otherwise you can create a [deploy key](https://docs.github.com/en/developers/overview/managing-deploy-keys) and keep it on the server.
#### Using Environment Variables in `deploy.toml`
-Similarly to `redwood.toml`, `deploy.toml` supports interpolation of environment variables. For more details on how to use the environment variable interpolation see [Using Environment Variables in redwood.toml](/docs/app-configuration-redwood-toml#using-environment-variables-in-redwoodtoml)
+Similarly to `redwood.toml`, `deploy.toml` supports interpolation of environment variables. For more details on how to use the environment variable interpolation see [Using Environment Variables in redwood.toml](/docs/app-configuration-redwood-toml#using-environment-variables-in-redwoodtoml)
#### Multiple Servers
@@ -278,7 +279,7 @@ You'll want to create an `.env` file in this directory containing any environmen
The deployment process uses a '[non-interactive](https://tldp.org/LDP/abs/html/intandnonint.html)' SSH session to run commands on the remote server. A non-interactive session will often load a minimal amount of settings for better compatibility and speed. In some versions of Linux `.bashrc` by default does not load (by design) from a non-interactive session. This can lead to `yarn` (or other commands) not being found by the deployment script, even though they are in your path, because additional ENV vars are set in `~/.bashrc` which provide things like NPM paths and setup.
-A quick fix on some distros is to edit the deployment user's `~/.bashrc` file and comment out the lines that *stop* non-interactive processing.
+A quick fix on some distros is to edit the deployment user's `~/.bashrc` file and comment out the lines that _stop_ non-interactive processing.
```diff title="~/.bashrc"
# If not running interactively, don't do anything
@@ -322,9 +323,9 @@ If it worked, hooray! You're deployed to BAREMETAL. If not, read on...
On the server you should see a new directory inside the `path` you defined in `deploy.toml`. It should be a timestamp of the deploy, like:
```bash
-drwxrwxr-x 7 ubuntu ubuntu 4096 Apr 22 23:00 ./
-drwxr-xr-x 7 ubuntu ubuntu 4096 Apr 22 22:46 ../
--rw-rw-r-- 1 ubuntu ubuntu 1167 Apr 22 20:49 .env
+drwxrwxr-x 7 ubuntu ubuntu 4096 Apr 22 23:00 ./
+drwxr-xr-x 7 ubuntu ubuntu 4096 Apr 22 22:46 ../
+-rw-rw-r-- 1 ubuntu ubuntu 1167 Apr 22 20:49 .env
drwxrwxr-x 10 ubuntu ubuntu 4096 Apr 22 21:43 20220422214218/
```
@@ -408,7 +409,7 @@ Note that if you have more than one process running, like we do here, requesting
## Starting Processes on Server Restart
-The `pm2` service requires some system "hooks" to be installed so it can boot up using your system's service manager. Otherwise, your PM2 services will need to be manually started again on a server restart. These steps only need to be run the first time you install PM2.
+The `pm2` service requires some system "hooks" to be installed so it can boot up using your system's service manager. Otherwise, your PM2 services will need to be manually started again on a server restart. These steps only need to be run the first time you install PM2.
SSH into your server and then run:
@@ -416,11 +417,11 @@ SSH into your server and then run:
pm2 startup
```
-You will see some output similar to the output below. We care about the output after "copy/paste the following command:" You'll need to do just that: copy the command starting with `sudo` and then paste and execute it. *Note* this command uses `sudo` so you'll need the root password to the machine in order for it to complete successfully.
+You will see some output similar to the output below. We care about the output after "copy/paste the following command:" You'll need to do just that: copy the command starting with `sudo` and then paste and execute it. _Note_ this command uses `sudo` so you'll need the root password to the machine in order for it to complete successfully.
:::warning
-The below text is *example* output, yours will be different, don't copy and paste ours!
+The below text is _example_ output, yours will be different, don't copy and paste ours!
:::
@@ -463,22 +464,22 @@ Baremetal supports running your own custom commands before or after the regular
You can define your before/after commands in three different places:
-* Globally - runs for any environment
-* Environment specific - runs for only a single environment
-* Server specific - runs for only a single server in a single environment
+- Globally - runs for any environment
+- Environment specific - runs for only a single environment
+- Server specific - runs for only a single server in a single environment
:::warning
Custom commands are run in the new **deploy** directory, not the root of your application directory. During a deploy the `current` symlink will point to the previous directory while your code is executed in the new one, before the `current` symlink location is updated.
```bash
-drwxrwxr-x 5 ubuntu ubuntu 4096 May 10 18:20 ./
-drwxr-xr-x 7 ubuntu ubuntu 4096 Apr 27 17:43 ../
-drwxrwxr-x 2 ubuntu ubuntu 4096 May 9 22:59 20220503211428/
-drwxrwxr-x 2 ubuntu ubuntu 4096 May 9 22:59 20220503211429/
-drwxrwxr-x 10 ubuntu ubuntu 4096 May 10 18:18 20220510181730/ <-- commands are run in here
-lrwxrwxrwx 1 ubuntu ubuntu 14 May 10 18:19 current -> 20220503211429/
--rw-rw-r-- 1 ubuntu ubuntu 1167 Apr 22 20:49 .env
+drwxrwxr-x 5 ubuntu ubuntu 4096 May 10 18:20 ./
+drwxr-xr-x 7 ubuntu ubuntu 4096 Apr 27 17:43 ../
+drwxrwxr-x 2 ubuntu ubuntu 4096 May 9 22:59 20220503211428/
+drwxrwxr-x 2 ubuntu ubuntu 4096 May 9 22:59 20220503211429/
+drwxrwxr-x 10 ubuntu ubuntu 4096 May 10 18:18 20220510181730/ commands are run in here < --
+lrwxrwxrwx 1 ubuntu ubuntu 14 May 10 18:19 current - > 20220503211429/
+-rw-rw-r-- 1 ubuntu ubuntu 1167 Apr 22 20:49 .env
```
:::
@@ -536,7 +537,6 @@ host = 'server.com'
You can include commands in any/all of the three configurations (global, env and server) and they will all be stacked up and run in that order: `global -> environment -> server`. For example:
-
```toml
[[production.servers]]
host = 'server.com'
@@ -571,7 +571,7 @@ You can even rollback multiple deploys, up to the total number you still have de
yarn rw deploy baremetal production --rollback 3
```
-Note that this will *not* rollback your database—if you had a release that changed the database, that updated database will still be in effect, but with the previous version of the web and api sides. Trying to undo database migrations is a very difficult proposition and isn't even possible in many cases.
+Note that this will _not_ rollback your database—if you had a release that changed the database, that updated database will still be in effect, but with the previous version of the web and api sides. Trying to undo database migrations is a very difficult proposition and isn't even possible in many cases.
Make sure to thoroughly test releases that change the database before doing it for real!
@@ -583,7 +583,7 @@ If you find that you have a particular complex deploy, one that may involve inco
yarn rw deploy baremetal production --maintenance up
```
-It does this by replacing `web/dist/200.html` with `web/src/maintenance.html`. This means any new web requests, at any URL, will show the maintenance page. This process also stops any services listed in the `processNames` option of `deploy.toml`—this is important for the api server as it will otherwise keep serving requests to users currently running the app, even though no *new* users can get the Javascript packages required to start a new session in their browser.
+It does this by replacing `web/dist/200.html` with `web/src/maintenance.html`. This means any new web requests, at any URL, will show the maintenance page. This process also stops any services listed in the `processNames` option of `deploy.toml`—this is important for the api server as it will otherwise keep serving requests to users currently running the app, even though no _new_ users can get the Javascript packages required to start a new session in their browser.
You can remove the maintenance page with:
@@ -613,7 +613,7 @@ The default configuration, which requires the least amount of manual configurati
### Redwood Serves Web and Api Sides, Bind to Port 80
-This is almost as easy as the default configuration, you just need to tell Redwood to bind to port 80. However, most *nix distributions will not allow a process to bind to ports lower than 1024 without root/sudo permissions. There is a command you can run to allow access to a specific binary (`node` in this case) to bind to one of those ports anyway.
+This is almost as easy as the default configuration, you just need to tell Redwood to bind to port 80. However, most \*nix distributions will not allow a process to bind to ports lower than 1024 without root/sudo permissions. There is a command you can run to allow access to a specific binary (`node` in this case) to bind to one of those ports anyway.
#### Tell Redwood to Bind to Port 80
@@ -729,8 +729,8 @@ module.exports = {
exec_mode: 'cluster',
wait_ready: true,
listen_timeout: 10000,
- }
- ]
+ },
+ ],
}
```
diff --git a/docs/docs/deploy/coherence.md b/docs/docs/deploy/coherence.md
index a2b9ec845d2a..ccd5bdc00088 100644
--- a/docs/docs/deploy/coherence.md
+++ b/docs/docs/deploy/coherence.md
@@ -34,6 +34,7 @@ yarn rw setup deploy coherence
The command will inspect your Prisma config to determine if you're using a supported database (at the moment, only `postgres` or `mysql` are supported on Coherence).
Then follow the [Coherence Redwood deploy docs](https://docs.withcoherence.com/docs/configuration/frameworks#redwood-js) for more information, including if you want to set up:
+
- a redis server
- database migration/seeding/snapshot loading
- cron jobs or async workers
diff --git a/docs/docs/deploy/edgio.md b/docs/docs/deploy/edgio.md
index 66df9c717529..fd115935b85b 100644
--- a/docs/docs/deploy/edgio.md
+++ b/docs/docs/deploy/edgio.md
@@ -1,13 +1,14 @@
# Deploy to Edgio
->⚠️ **Deprecated**
+> ⚠️ **Deprecated**
>
->As of Redwood v7, we are deprecating this deploy setup as an "officially" supported provider. This means:
->- For projects already using this deploy provider, there will be NO change at this time
->- Both the associated `setup` and `deploy` commands will remain in the framework as is; when setup is run, there will be a “deprecation” message
->- We will no longer run CI/CD on the Edgio deployments, which means we are no longer guaranteeing this deploy works with each new version
+> As of Redwood v7, we are deprecating this deploy setup as an "officially" supported provider. This means:
>
->If you have concerns or questions about our decision to deprecate this deploy provider please reach out to us on our [community forum](https://community.redwoodjs.com).
+> - For projects already using this deploy provider, there will be NO change at this time
+> - Both the associated `setup` and `deploy` commands will remain in the framework as is; when setup is run, there will be a “deprecation” message
+> - We will no longer run CI/CD on the Edgio deployments, which means we are no longer guaranteeing this deploy works with each new version
+>
+> If you have concerns or questions about our decision to deprecate this deploy provider please reach out to us on our [community forum](https://community.redwoodjs.com).
[Edgio](https://edg.io) extends the capabilities of a traditional CDN by not only hosting your static content, but also providing server-side rendering for progressive web applications as well as caching both your APIs and HTML at the network edge to provide your users with the fastest browsing experience.
@@ -18,8 +19,8 @@ In order to deploy your RedwoodJS project to Edgio, the project must first be in
1. In your project, run the command `yarn rw setup deploy edgio`.
2. Verify the changes to your project, commit and push to your repository.
3. Deploy your project to Edgio
- 1. If this is your first time deploying to Edgio, the interactive CLI will prompt to authenticate using your browser. You can start the deploy by running `yarn rw deploy edgio`.
- 2. If you are deploying from a **non-interactive** environment, you will need to create an account on [Edgio Developer Console](https://app.layer0.co) first and setup a [deploy token](https://docs.edg.io/guides/deploy_apps#deploy-from-ci). Once the deploy token is created, save it as a secret to your environment. You can start the deploy by running `yarn rw deploy edgio --token=XXX`.
-4. Follow the link in the output to view your site live once deployment has completed!
+4. If this is your first time deploying to Edgio, the interactive CLI will prompt to authenticate using your browser. You can start the deploy by running `yarn rw deploy edgio`.
+5. If you are deploying from a **non-interactive** environment, you will need to create an account on [Edgio Developer Console](https://app.layer0.co) first and setup a [deploy token](https://docs.edg.io/guides/deploy_apps#deploy-from-ci). Once the deploy token is created, save it as a secret to your environment. You can start the deploy by running `yarn rw deploy edgio --token=XXX`.
+6. Follow the link in the output to view your site live once deployment has completed!
For more information on deploying to Edgio, check out the [documentation](https://docs.edg.io).
diff --git a/docs/docs/deploy/flightcontrol.md b/docs/docs/deploy/flightcontrol.md
index ffcb0409c42e..9d1c544703a3 100644
--- a/docs/docs/deploy/flightcontrol.md
+++ b/docs/docs/deploy/flightcontrol.md
@@ -13,11 +13,11 @@ description: How to deploy a Redwood app to AWS via Flightcontrol
3. Commit the changes and push to github.
4. If you don't have an account, sign up at [app.flightcontrol.dev/signup](https://app.flightcontrol.dev/signup?ref=redwood).
5. Create a new project from the onboarding screen or project list.
- 1. Connect your Github account and select your repo.
- 2. Click "Create Project" and complete any required steps like linking your AWS account.
- 3. Configuration Type should autoselect as `flightcontrol.json`.
- 4. NOTE: `flightcontrol.json` is now the source of truth for your Project and its Environments.
+ 1. Connect your Github account and select your repo.
+ 2. Click "Create Project" and complete any required steps like linking your AWS account.
+ 3. Configuration Type should autoselect as `flightcontrol.json`.
+ 4. NOTE: `flightcontrol.json` is now the source of truth for your Project and its Environments.
6. Add your env vars in Flightcontrol for your Environment.
7. If using dbAuth, add the session secret key env variable in the Flightcontrol dashboard.
-If you have *any* problems or questions, Flightcontrol is very responsive in [their support Discord](https://discord.gg/yY8rSPrD6q).
+If you have _any_ problems or questions, Flightcontrol is very responsive in [their support Discord](https://discord.gg/yY8rSPrD6q).
diff --git a/docs/docs/deploy/introduction.md b/docs/docs/deploy/introduction.md
index 1cbb38e0fcd7..49eba8f4a87d 100644
--- a/docs/docs/deploy/introduction.md
+++ b/docs/docs/deploy/introduction.md
@@ -12,6 +12,7 @@ Redwood is designed for both serverless and traditional infrastructure deploymen
4. the hosting provider deploys the built Web static assets to a CDN and the API code to a serverless backend (e.g. AWS Lambdas)
Currently, these are the officially supported deploy targets:
+
- Baremetal (physical server that you have SSH access to)
- [Coherence](https://www.withcoherence.com/)
- [Flightcontrol.dev](https://www.flightcontrol.dev?ref=redwood)
@@ -22,13 +23,13 @@ Currently, these are the officially supported deploy targets:
- [Vercel.com](https://vercel.com)
Redwood has a CLI generator that adds the code and configuration required by the specified provider (see the [CLI Doc](cli-commands.md#deploy-config) for more information):
+
```shell
yarn rw setup deploy
```
There are examples of deploying Redwood on other providers such as Google Cloud and direct to AWS. You can find more information by searching the [GitHub Issues](https://github.com/redwoodjs/redwood/issues) and [Forums](https://community.redwoodjs.com).
-
## General Deployment Setup
Deploying Redwood requires setup for the following four categories.
diff --git a/docs/docs/deploy/netlify.md b/docs/docs/deploy/netlify.md
index ad62b9b5d8f5..3f779f1b6ab4 100644
--- a/docs/docs/deploy/netlify.md
+++ b/docs/docs/deploy/netlify.md
@@ -18,7 +18,6 @@ While you may be tempted to use the [Netlify CLI](https://cli.netlify.com) comma
The main reason for this is that these Netlify CLI commands simply build and deploy -- they build your project locally and then push the dist folder. That means that when building a RedwoodJS project, the [Prisma client is generated with binaries matching the operating system at build time](https://cli.netlify.com/commands/link) -- and not the [OS compatible](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#binarytargets-options) with running functions on Netlify. Your Prisma client engine may be `darwin` for OSX or `windows` for Windows, but it needs to be `debian-openssl-1.1.x` or `rhel-openssl-1.1.x`. If the client is incompatible, your functions will fail.
-
Therefore, **please follow the [Tutorial Deployment section](tutorial/chapter4/deployment.md)** to sync your GitHub (or other compatible source control service) repository with Netlify andalllow their build and deploy system to manage deployments.
:::
diff --git a/docs/docs/deploy/render.md b/docs/docs/deploy/render.md
index 705ec3dec80f..46ae652e35e8 100644
--- a/docs/docs/deploy/render.md
+++ b/docs/docs/deploy/render.md
@@ -9,6 +9,7 @@ Render is a unified cloud to build and run all your apps and websites with free
## Render tl;dr Deploy
If you simply want to experience the Render deployment process, including a Postgres or SQLite database, you can do the following:
+
1. create a new redwood project: `yarn create redwood-app ./render-deploy`
2. after your "render-deploy" project installation is complete, init git, commit, and add it as a new repo to GitHub or GitLab
3. run the command `yarn rw setup deploy render`, use the flag `--database` to select from `postgresql`, `sqlite` or `none` to proceed without a database [default : `postgresql`]
diff --git a/docs/docs/deploy/serverless.md b/docs/docs/deploy/serverless.md
index d854ff791f7c..ed9a1d108ca4 100644
--- a/docs/docs/deploy/serverless.md
+++ b/docs/docs/deploy/serverless.md
@@ -4,16 +4,17 @@ description: Deploy to AWS with Serverless Framework
# Deploy to AWS with Serverless Framework
->⚠️ **Deprecated**
->As of Redwood v5, we are deprecating this deploy setup as an "officially" supported provider. This means:
->- For projects already using this deploy provider, there will be NO change at this time
->- Both the associated `setup` and `deploy` commands will remain in the framework as is; when setup is run, there will be a “deprecation” message
->- We will no longer run CI/CD on the Serverless-AWS deployments, which means we are no longer guaranteeing this deploy works with each new version
->- We are exploring better options to deploy directly to AWS Lambdas; the current deploy commands will not be removed until we find a replacement
+> ⚠️ **Deprecated**
+> As of Redwood v5, we are deprecating this deploy setup as an "officially" supported provider. This means:
>
->For more details (e.g. why?) and current status, see the Forum post ["Deprecating support for Serverless Framework Deployments to AWS Lambdas"](https://community.redwoodjs.com/t/deprecating-support-for-serverless-framework-deployments-to-aws-lambdas/4755/10)
+> - For projects already using this deploy provider, there will be NO change at this time
+> - Both the associated `setup` and `deploy` commands will remain in the framework as is; when setup is run, there will be a “deprecation” message
+> - We will no longer run CI/CD on the Serverless-AWS deployments, which means we are no longer guaranteeing this deploy works with each new version
+> - We are exploring better options to deploy directly to AWS Lambdas; the current deploy commands will not be removed until we find a replacement
+>
+> For more details (e.g. why?) and current status, see the Forum post ["Deprecating support for Serverless Framework Deployments to AWS Lambdas"](https://community.redwoodjs.com/t/deprecating-support-for-serverless-framework-deployments-to-aws-lambdas/4755/10)
->The following instructions assume you have read the [General Deployment Setup](./introduction.md#general-deployment-setup) section above.
+> The following instructions assume you have read the [General Deployment Setup](./introduction.md#general-deployment-setup) section above.
Yes, the name is confusing, but Serverless provides a very interesting option—deploy to your own cloud service account and skip the middleman entirely! By default, Serverless just orchestrates starting up services in your cloud provider of choice and pushing your code up to them. Any bill you receive is from your hosting provider (although many offer a generous free tier). You can optionally use the [Serverless Dashboard](https://www.serverless.com/dashboard/) to monitor your deploys and setup CI/CD to automatically deploy when pushing to your repo of choice. If you don't setup CI/CD you actually deploy from your development machine (or another designated machine you've setup to do the deployment).
@@ -64,7 +65,6 @@ Once that command completes you should see a message including the URL of your s
From now on you can simply run `yarn rw deploy serverless` when you're ready to deploy (which will also be much faster).
-
:::info
Remember, if you add or generate new serverless functions (or endpoints), you'll need to update the configuration in your serverless.yml in `./api/serverless.yml`.
@@ -88,7 +88,7 @@ There are even more places you can get environment variables from, check out Ser
To integrate your site into the Serverless Dashboard, there are two ways:
-1. Run `yarn serverless login` and a browser *should* open asking you to allow permission. However, in our experience, this command will fail nearly 50% of the time complaining about an invalid URL. If it *does* work you can then run `yarn serverless` in both the `api` and `web` directories to link to them an existing app in the Dashboard, or you'll be prompted to create a new one. Future deploys will now be monitored on the Dashboard.
+1. Run `yarn serverless login` and a browser _should_ open asking you to allow permission. However, in our experience, this command will fail nearly 50% of the time complaining about an invalid URL. If it _does_ work you can then run `yarn serverless` in both the `api` and `web` directories to link to them an existing app in the Dashboard, or you'll be prompted to create a new one. Future deploys will now be monitored on the Dashboard.
2. You can manually add the `org` and `app` lines in `api/serverless.yml` and `web/serverless.yml`. You'll see example ones commented out near the top of the file.
## Environments Besides Production
diff --git a/docs/docs/deploy/vercel.md b/docs/docs/deploy/vercel.md
index 5720c2225b5c..060627e4c224 100644
--- a/docs/docs/deploy/vercel.md
+++ b/docs/docs/deploy/vercel.md
@@ -4,11 +4,12 @@ description: Deploy serverless in an instant with Vercel
# Deploy to Vercel
->The following instructions assume you have read the [General Deployment Setup](./introduction.md#general-deployment-setup) section above.
+> The following instructions assume you have read the [General Deployment Setup](./introduction.md#general-deployment-setup) section above.
## Vercel tl;dr Deploy
If you simply want to experience the Vercel deployment process without a database and/or adding custom code, you can do the following:
+
1. create a new redwood project: `yarn create redwood-app ./vercel-deploy`
2. after your "vercel-deploy" project installation is complete, init git, commit, and add it as a new repo to GitHub, BitBucket, or GitLab
3. run the command `yarn rw setup deploy vercel` and commit and push changes
@@ -29,6 +30,7 @@ Complete the following two steps. Then save, commit, and push your changes.
### Step 1. Serverless Functions Path
Run the following CLI Command:
+
```shell
yarn rw setup deploy vercel
```
@@ -50,6 +52,7 @@ yarn rw deploy vercel --prisma=false --data-migrate=false
:::
### Vercel Initial Setup and Configuration
+
Either [login](https://vercel.com/login) to your Vercel account and select "Import Project" or use the Vercel [quick start](https://vercel.com/#get-started).
Then select the "Continue" button within the "From Git Repository" section:
@@ -60,6 +63,7 @@ Next, select the provider where your repo is hosted: GitHub, GitLab, or Bitbucke
You'll then need to provide permissions for Vercel to access the repo on your hosting provider.
### Import and Deploy your Project
+
Vercel will recognize your repo as a Redwood project and take care of most configuration heavy lifting. You should see the following options and, most importantly, the "Framework Preset" showing RedwoodJS.
@@ -121,13 +125,13 @@ export const handler = async (event: APIGatewayEvent, _context: Context) => {
}),
}
}
-
```
:::tip important
Since Redwood has it's own handling of the api directory, the Vercel flavored api directory is disabled. Therefore you don't use the "functions" config in `vercel.json` with Redwood.
Also, be sure to use Node version 20.x or greater or set the `runtime` in the function config:
+
```ts
export const config = {
runtime: 'nodejs20.x',
diff --git a/docs/docs/directives.md b/docs/docs/directives.md
index c4b9e97f403a..d9c0d1d50119 100644
--- a/docs/docs/directives.md
+++ b/docs/docs/directives.md
@@ -251,14 +251,14 @@ As noted in the [GraphQL spec](https://graphql.org/learn/queries/#directives):
Here's a helpful guide for deciding when you should use one of Redwood's Validator or Transformer directives:
-| | Use | Directive | Custom? | Type |
-| --- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------ |
-| ✅ | Check if the request is authenticated? | `@requireAuth` | Built-in | Validator |
-| ✅ | Check if the user belongs to a role? | `@requireAuth(roles: ["AUTHOR"])` | Built-in | Validator |
-| ✅ | Only allow admins to see emails, but others get a masked value like "###@######.###" | `@maskedEmail(roles: ["ADMIN"])` | Custom | Transformer |
-| 🙅 | Know if the logged in user can edit the record, and/or values | N/A - Instead do this check in your service |
+| | Use | Directive | Custom? | Type |
+| --- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------ |
+| ✅ | Check if the request is authenticated? | `@requireAuth` | Built-in | Validator |
+| ✅ | Check if the user belongs to a role? | `@requireAuth(roles: ["AUTHOR"])` | Built-in | Validator |
+| ✅ | Only allow admins to see emails, but others get a masked value like "###@######.###" | `@maskedEmail(roles: ["ADMIN"])` | Custom | Transformer |
+| 🙅 | Know if the logged in user can edit the record, and/or values | N/A - Instead do this check in your service |
| 🙅 | Is my input a valid email address format? | N/A - Instead do this check in your service using [Service Validations](services.md#service-validations) or consider [GraphQL Scalars](https://www.graphql-scalars.dev) |
-| 🙅 | I want to remove a field from the response for data filtering; for example, do not include the title of the post | `@skip(if: true )` or `@include(if: false)` | Instead use [core directives](https://graphql.org/learn/queries/#directives) on the GraphQL client query, not the SDL | Core GraphQL |
+| 🙅 | I want to remove a field from the response for data filtering; for example, do not include the title of the post | `@skip(if: true )` or `@include(if: false)` | Instead use [core directives](https://graphql.org/learn/queries/#directives) on the GraphQL client query, not the SDL | Core GraphQL |
## Combining, Chaining and Cascading Directives
@@ -412,11 +412,11 @@ When your app builds and your server starts up, Redwood checks that **all** quer
If not, then your build will fail:
```bash
- ✖ Verifying graphql schema...
- Building API...
- Cleaning Web...
- Building Web...
- Prerendering Web...
+✖ Verifying graphql schema...
+Building API...
+Cleaning Web...
+Building Web...
+Prerendering Web...
You must specify one of @requireAuth, @skipAuth or a custom directive for
- contacts Query
- posts Query
@@ -490,8 +490,8 @@ you'll be presented with a choice of creating a Validator or a Transformer direc
yarn redwood generate directive myDirective
? What type of directive would you like to generate? › - Use arrow-keys. Return to submit.
-❯ Validator - Implement a validation: throw an error if criteria not met to stop execution
- Transformer - Modify values of fields or query responses
+❯ Validator - Implement a validation: throw an error if criteria not met to stop execution
+Transformer - Modify values of fields or query responses
```
> **Note:** You can pass the `--type` flag with either `validator` or `transformer` to create the desired directive type.
@@ -586,7 +586,9 @@ describe('isSubscriber directive', () => {
it('has a isSubscriber throws an error if validation does not pass', () => {
const mockExecution = mockRedwoodDirective(isSubscriber, {})
- expect(mockExecution).toThrowError('Implementation missing for isSubscriber')
+ expect(mockExecution).toThrowError(
+ 'Implementation missing for isSubscriber'
+ )
})
})
```
@@ -675,7 +677,6 @@ describe('maskedEmail directive', () => {
If your Transformer Directive is asynchronous, you can use `resolves` to handle the result.
-
```ts
import maskedEmail from './maskedEmail'
@@ -689,4 +690,5 @@ describe('maskedEmail directive', () => {
})
})
```
+
:::
diff --git a/docs/docs/docker.md b/docs/docs/docker.md
index 70a534e8703e..f2d013a56b93 100644
--- a/docs/docs/docker.md
+++ b/docs/docs/docker.md
@@ -15,6 +15,7 @@ yarn rw setup docker
```
The setup commands does several things:
+
- writes four files: `Dockerfile`, `.dockerignore`, `docker-compose.dev.yml`, and `docker-compose.prod.yml`
- adds the `@redwoodjs/api-server` and `@redwoodjs/web-server` packages to the api and web sides respectively
- edits the `browser.open` setting in the `redwood.toml` (right now, if it's set to `true`, it'll break the dev server when running the `docker-compose.dev.yml`)
@@ -52,7 +53,7 @@ root@...:/home/node/app# yarn rw prisma migrate dev
The docker setup command assumes that you are using Postgres as your database provider and sets up a local Postgres database for you. You may have to switch from SQLite to Postgres if you have not done so and want to continue with the default setup.
:::
-:::important
+:::important
If you are using a [Server File](#using-the-server-file) then you should [change the command](#command) that runs the `api_serve` service.
:::
@@ -131,7 +132,7 @@ COPY --chown=node:node yarn.lock .
Here we copy the minimum set of files that the `yarn install` step needs.
The order isn't completely arbitrary—it tries to maximize [Docker's layer caching](https://docs.docker.com/build/cache/).
-We expect `yarn.lock` to change more than the `package.json`s and the `package.json`s to change more than `.yarnrc.yml`.
+We expect `yarn.lock` to change more than the `package.json`s and the `package.json`s to change more than `.yarnrc.yml`.
That said, it's hard to argue that these files couldn't be arranged differently, or that the `COPY` instructions couldn't be combined.
The important thing is that they're all here, before the `yarn install` step:
@@ -275,19 +276,16 @@ ENV NODE_ENV=production
CMD [ "node_modules/.bin/rw-server", "api" ]
```
-:::important
+:::important
If you are using a [Server File](#using-the-server-file) then you must change the command that runs the `api_serve` service to `./api/dist/server.js` as shown above.
Not updating the command will not completely configure the GraphQL Server and not setup [Redwood Realtime](./realtime.md), if you are using that.
:::
-
Note that the Redwood CLI isn't available anymore because it is a dev dependency.
To access the server bin, we have to find its path in `node_modules`.
Though this is somewhat discouraged in modern yarn, since we're using the `node-modules` node linker, it's in `node_modules/.bin`.
-
-
### The `web_build` stage
This `web_build` builds the web side:
@@ -370,7 +368,7 @@ Lastly, note that we use the shell form of `CMD` here for its variable expansion
The `console` stage is an optional stage for debugging:
-```Dockerfile
+````Dockerfile
FROM base as console
# To add more packages:
@@ -387,7 +385,7 @@ FROM base as console
COPY --chown=node:node api api
COPY --chown=node:node web web
COPY --chown=node:node scripts scripts
-```
+````
The console stage completes the base stage by copying in the rest of your Redwood app.
But then it pretty much leaves you to your own devices.
@@ -529,13 +527,11 @@ yarn node api/dist/server.js
You can't run the server file directly with Node.js; it has to be built first:
-
```
yarn rw build api
```
-The api serve stage in the Dockerfile pulls from the api build stage, so things are already in the right order there. Similarly, for `yarn rw dev`, the dev server will build and reload the server file for you.
-
+The api serve stage in the Dockerfile pulls from the api build stage, so things are already in the right order there. Similarly, for `yarn rw dev`, the dev server will build and reload the server file for you.
### Command
@@ -548,7 +544,7 @@ That means you will swap the `CMD` instruction in the api server stage:
+ CMD [ "api/dist/server.js" ]
```
-:::important
+:::important
If you are using a [Server File](#using-the-server-file) then you must change the command that runs the `api_serve` service to `./api/dist/server.js` as shown above.
Not updating the command will not completely configure the GraphQL Server and not setup [Redwood Realtime](./realtime.md), if you are using that.
@@ -559,6 +555,7 @@ Not updating the command will not completely configure the GraphQL Server and no
There are three ways you may wish to configure the server.
#### Underlying Fastify server
+
First, you can configure how the underlying Fastify server is instantiated via the`fastifyServerOptions` passed to the `createServer` function:
```ts title="api/src/server.ts"
@@ -567,7 +564,7 @@ const server = await createServer({
// highlight-start
fastifyServerOptions: {
// ...
- }
+ },
// highlight-end
})
```
@@ -575,6 +572,7 @@ const server = await createServer({
For the complete list of options, see [Fastify's documentation](https://fastify.dev/docs/latest/Reference/Server/#factory).
#### Configure the redwood API plugin
+
Second, you may want to alter the behavior of redwood's API plugin itself. To do this we provide a `configureApiServer(server)` option where you can do anything you wish to the fastify instance before the API plugin is registered. Two examples are given below.
##### Example: Compressing Payloads and Rate Limiting
@@ -601,14 +599,13 @@ const server = await createServer({
threshold: 1024,
encodings: ['deflate', 'gzip'],
})
-
+
await server.register(import('@fastify/rate-limit'), {
max: 100,
timeWindow: '5 minutes',
})
- }
+ },
})
-
```
##### Example: File Uploads
@@ -625,7 +622,7 @@ If you try to POST file content to the api server such as images or PDFs, you ma
```
This's because Fastify [only supports `application/json` and `text/plain` content types natively](https://www.fastify.io/docs/latest/Reference/ContentTypeParser/).
-While Redwood configures the api server to also accept `application/x-www-form-urlencoded` and `multipart/form-data`, if you want to support other content or MIME types (likes images or PDFs), you'll need to configure them here in the server file.
+While Redwood configures the api server to also accept `application/x-www-form-urlencoded` and `multipart/form-data`, if you want to support other content or MIME types (likes images or PDFs), you'll need to configure them here in the server file.
You can use Fastify's `addContentTypeParser` function to allow uploads of the content types your application needs.
For example, to support image file uploads you'd tell Fastify to allow `/^image\/.*/` content types:
@@ -633,13 +630,13 @@ For example, to support image file uploads you'd tell Fastify to allow `/^image\
```ts title="api/src/server.ts"
const server = await createServer({
logger,
- configureApiServer(server){
+ configureApiServer(server) {
server.addContentTypeParser(/^image\/.*/, (_req, payload, done) => {
payload.on('end', () => {
done()
})
})
- }
+ },
})
```
@@ -648,6 +645,7 @@ The regular expression (`/^image\/.*/`) above allows all image content or MIME t
Now, when you POST those content types to a function served by the api server, you can access the file content on `event.body`.
#### Additional Fastify plugins
+
Finally, you can register additional Fastify plugins on the server instance:
```ts title="api/src/server.ts"
@@ -657,11 +655,11 @@ const server = await createServer({
// highlight-next-line
server.register(myFastifyPlugin)
-```
+```
:::note Fastify encapsulation
-Fastify is built around the concept of [encapsulation](https://fastify.dev/docs/latest/Reference/Encapsulation/). It is important to note that redwood's API plugin cannot be mutated after it is registered, see [here](https://fastify.dev/docs/latest/Reference/Plugins/#asyncawait). This is why you must use the `configureApiServer` option to do as shown above.
+Fastify is built around the concept of [encapsulation](https://fastify.dev/docs/latest/Reference/Encapsulation/). It is important to note that redwood's API plugin cannot be mutated after it is registered, see [here](https://fastify.dev/docs/latest/Reference/Plugins/#asyncawait). This is why you must use the `configureApiServer` option to do as shown above.
:::
@@ -678,25 +676,25 @@ It takes the same arguments as `listen`, except for host and port. It computes t
1. `--apiHost` or `--apiPort` flags:
- ```
- yarn node api/dist/server.js --apiHost 0.0.0.0 --apiPort 8913
- ```
+```
+yarn node api/dist/server.js --apiHost 0.0.0.0 --apiPort 8913
+```
2. `REDWOOD_API_HOST` or `REDWOOD_API_PORT` env vars:
- ```
- export REDWOOD_API_HOST='0.0.0.0'
- export REDWOOD_API_PORT='8913'
- yarn node api/dist/server.js
- ```
+```
+export REDWOOD_API_HOST='0.0.0.0'
+export REDWOOD_API_PORT='8913'
+yarn node api/dist/server.js
+```
3. `[api].host` and `[api].port` in `redwood.toml`:
- ```toml title="redwood.toml"
- [api]
- host = '0.0.0.0'
- port = 8913
- ```
+```toml title="redwood.toml"
+[api]
+ host = '0.0.0.0'
+ port = 8913
+```
If you'd rather not have `createServer` parsing `process.argv`, you can disable it via `parseArgv`:
diff --git a/docs/docs/environment-variables.md b/docs/docs/environment-variables.md
index 537d7d259930..1e6534bbdc60 100644
--- a/docs/docs/environment-variables.md
+++ b/docs/docs/environment-variables.md
@@ -22,6 +22,7 @@ Redwood also configures Vite, so that all references to `process.env` vars on th
## Web
### Including environment variables
+
> **Heads Up:** for Web to access environment variables in production, you _must_ configure one of the options below.
>
> Redwood recommends **Option 1: `redwood.toml`** as it is the most robust.
@@ -47,13 +48,12 @@ By adding environment variables to this array, they'll be available to Web in pr
Note: if someone inspects your site's source, _they could see your `REDWOOD_ENV_SECRET_API_KEY` in plain text._ This is a limitation of delivering static JS and HTML to the browser.
-#### Option 2: Prefixing with REDWOOD\_ENV\_
+#### Option 2: Prefixing with REDWOOD_ENV\_
In `.env`, if you prefix your environment variables with `REDWOOD_ENV_`, they'll be available via `process.env.REDWOOD_ENV_MY_VAR_NAME`, and will be dynamically replaced at build-time.
Like the option above, these are also removed and replaced with the _actual value_ during build in order to be available in production.
-
### Accessing API URLs
Redwood automatically makes your API URL configurations from the web section of your `redwood.toml` available globally.
diff --git a/docs/docs/forms.md b/docs/docs/forms.md
index db7b39ce2657..22575b27c69e 100644
--- a/docs/docs/forms.md
+++ b/docs/docs/forms.md
@@ -26,7 +26,7 @@ import {
`@redwoodjs/forms` exports the following components:
| Component | Description |
-|:------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------|
+| :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
| `
))
- ```
+```
Note that if you're copy-pasting this example, it uses [Tailwind CSS](https://tailwindcss.com), so you'll have to set that up first. See the [setup ui](./cli-commands.md#setup-ui) CLI command to add it to your project.
diff --git a/docs/docs/schema-relations.md b/docs/docs/schema-relations.md
index 0d0b3add404b..c5e28261c00d 100644
--- a/docs/docs/schema-relations.md
+++ b/docs/docs/schema-relations.md
@@ -45,7 +45,7 @@ These relationships can be [implicit](https://www.prisma.io/docs/concepts/compon
CRUD (Create, Retrieve, Update, Delete) actions in Redwood currently require a single, unique field in order to retrieve, update or delete a record. This field must be denoted with Prisma's [`@id`](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#id) attribute, marking it as the tables's primary key. This field is guaranteed to be unique and so can be used to find a specific record.
-Prisma's implicit many-to-many relationships create a table _without_ a single field marked with the `@id` attribute. Instead, it uses a similar attribute: [`@@id`](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#id-1) to define a *multi-field ID*. This multi-field ID will become the tables's primary key. The diagram above shows the result of letting Prisma create an implicit relationship.
+Prisma's implicit many-to-many relationships create a table _without_ a single field marked with the `@id` attribute. Instead, it uses a similar attribute: [`@@id`](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#id-1) to define a _multi-field ID_. This multi-field ID will become the tables's primary key. The diagram above shows the result of letting Prisma create an implicit relationship.
Since there's no single `@id` field in implicit many-to-many relationships, you can't use the SDL generator with the `--crud` flag. Likewise, you can't use the scaffold generator, which uses the SDL generator (with `--crud`) behind the scenes.
@@ -131,11 +131,11 @@ yarn rw g sdl Book
Here's how the output from the command starts:
```bash
- ✔ Generating SDL files...
- ✔ Successfully wrote file `./api/src/graphql/books.sdl.js`
- ✔ Successfully wrote file `./api/src/services/books/books.scenarios.js`
- ✔ Successfully wrote file `./api/src/services/books/books.test.js`
- ✔ Successfully wrote file `./api/src/services/books/books.js`
+✔ Generating SDL files...
+✔ Successfully wrote file $(./api/src/graphql/books.sdl.js)
+✔ Successfully wrote file $(./api/src/services/books/books.scenarios.js)
+✔ Successfully wrote file $(./api/src/services/books/books.test.js)
+✔ Successfully wrote file $(./api/src/services/books/books.js)
```
Looks like it's working so far. The SDL and service files generated!
@@ -213,10 +213,10 @@ yarn rw g sdl Shelf --force --no-tests
[Self-relations](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/self-relations#one-to-many-self-relations) are useful for modeling parent-child relationships where the parent and child are the "same type of thing".
For example, in a business, everyone is an employee with a role and possibly someone to directly report to:
-* President—no direct report (for the purposes of this example)
-* Director—reports to the President
-* Manager—reports to a Director
-* Employee—reports to a Manager, but has no direct reports
+- President—no direct report (for the purposes of this example)
+- Director—reports to the President
+- Manager—reports to a Director
+- Employee—reports to a Manager, but has no direct reports
Let's use a self-relation to model this in our Prisma schema:
diff --git a/docs/docs/security.md b/docs/docs/security.md
index 1493da1f2d10..d77502550439 100644
--- a/docs/docs/security.md
+++ b/docs/docs/security.md
@@ -6,12 +6,12 @@ description: Build and deploy secure applications
RedwoodJS wants you to be able build and deploy secure applications and takes the topic of security seriously.
-* [RedwoodJS Security](https://github.com/redwoodjs/redwood/security) on GitHub
-* [CodeQL code scanning](https://github.com/features/security)
-* [Authentication](authentication.md)
-* [Webhook signature verification](webhooks.md)
-* [Ways to keep your serverless functions secure](serverless-functions.md#security-considerations)
-* [Environment variables for secure keys and tokens](environment-variables.md)
+- [RedwoodJS Security](https://github.com/redwoodjs/redwood/security) on GitHub
+- [CodeQL code scanning](https://github.com/features/security)
+- [Authentication](authentication.md)
+- [Webhook signature verification](webhooks.md)
+- [Ways to keep your serverless functions secure](serverless-functions.md#security-considerations)
+- [Environment variables for secure keys and tokens](environment-variables.md)
> ⚠️ **Security is Your Responsibility**
> While Redwood offers the tools, practices, and information to keep your application secure, it remains your responsibility to put these in place. Proper password, token, and key protection using disciplined communication, password management systems, and environment management services like [Doppler](https://www.doppler.com) are strongly encouraged.
@@ -25,15 +25,15 @@ RedwoodJS wants you to be able build and deploy secure applications and takes th
`@redwoodjs/auth` is a lightweight wrapper around popular SPA authentication libraries. We currently support [the following authentication providers](authentication.md) as well as a self-hosted solution ([dbAuth](auth/dbauth.md)):
-* Netlify Identity Widget
-* Auth0
-* Azure Active Directory
-* Netlify GoTrue-JS
-* Magic Links - Magic.js
-* Firebase's GoogleAuthProvider
-* Ethereum
-* Supabase
-* Nhost
+- Netlify Identity Widget
+- Auth0
+- Azure Active Directory
+- Netlify GoTrue-JS
+- Magic Links - Magic.js
+- Firebase's GoogleAuthProvider
+- Ethereum
+- Supabase
+- Nhost
For example implementations, please see [Authentication](https://github.com/redwoodjs/redwood/tree/main/packages/auth) and the use of the `getCurrentUser` and `requireAuth` helpers.
@@ -46,12 +46,15 @@ GraphQL is a fundamental part of Redwood. For details on how Redwood uses GraphQ
### Malicious Document Requests
The RedwoodJS GraphQL handler sets [reasonable defaults](graphql.md#security) to prevent abusive queries that attackers often use to exploit systems.
+
### Disable Introspection and Playground
Because both introspection and the playground share possibly sensitive information about your data model, your data, your queries and mutations, best practices for deploying a GraphQL Server call to [disable these in production](graphql.md#introspection-and-playground-disabled-in-production), by default RedwoodJS **only enables introspection and the playground when running in development**.
:::note
+
+
For more information on how to enable introspection in production, please see the [GraphQL Docs](graphql.md#introspection-and-playground-disabled-in-production).
:::
@@ -69,9 +72,9 @@ They are a form of messaging or automation and allows web applications to commun
Since each of these webhooks will call a function endpoint in your RedwoodJS api, you need to ensure that these run **only when they should**. That means you need to:
-* Verify it comes from the place you expect
-* Trust the party
-* Know the payload sent in the hook hasn't been tampered with
-* Ensure that the hook isn't reprocessed or replayed
+- Verify it comes from the place you expect
+- Trust the party
+- Know the payload sent in the hook hasn't been tampered with
+- Ensure that the hook isn't reprocessed or replayed
For details on how to keep your incoming webhooks secure and how to sign your outgoing webhooks, please see [Webhooks](webhooks.md).
diff --git a/docs/docs/serverless-functions.md b/docs/docs/serverless-functions.md
index c0c53fc8a846..a5753b376d81 100644
--- a/docs/docs/serverless-functions.md
+++ b/docs/docs/serverless-functions.md
@@ -6,7 +6,6 @@ description: Create, develop, and run serverless functions
-
:::info
You can think of serverless functions as API Endpoints, and in the future we'll update the terminology used.
@@ -79,10 +78,10 @@ api/src
├── functions
│ ├── graphql.ts
│ └── helloWorld
-│ ├── helloWorld.scenarios.ts
-│ ├── helloWorld.test.ts
-│ └── helloWorld.ts # <-- imports hellWorldLib
-│ └── helloWorldLib.ts # <-- exports can be used in the helloWorld
+│ ├── helloWorld.scenarios.ts
+│ ├── helloWorld.test.ts
+│ └── helloWorld.ts # <-- imports hellWorldLib
+│ └── helloWorldLib.ts # <-- exports can be used in the helloWorld
```
## Developing locally
@@ -104,7 +103,7 @@ To help you mock the `event` and `context` information, we've provided several a
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `mockHttpEvent` | Use this to mock out the http request `event` that is received by your function in unit tests. Here you can set `headers`, `httpMethod`, `queryStringParameters` as well as the `body` and if the body `isBase64Encoded`. The `event` contains information from the invoker as JSON-formatted string whose structure will vary. See [Working with AWS Lambda proxy integrations for HTTP APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) for the payload format. |
| `mockContext` | Use this function to mock the http `context`. Your function handler receives a context object with properties that provide information about the invocation, function, and execution environment. See [AWS Lambda context object in Node.js](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-context.html) for what context properties you can mock. |
-| `mockSignedWebhook` | Use this function to mock a signed webhook. This is a specialized `mockHttpEvent` mock that also signs the payload and adds a signature header needed to verify that the webhook is trustworthy. See [How to Receive and Verify an Incoming Webhook](webhooks.md#how-to-receive-and-verify-an-incoming-webhook) to learn more about signing and verifying webhooks. |
+| `mockSignedWebhook` | Use this function to mock a signed webhook. This is a specialized `mockHttpEvent` mock that also signs the payload and adds a signature header needed to verify that the webhook is trustworthy. See [How to Receive and Verify an Incoming Webhook](webhooks.md#how-to-receive-and-verify-an-incoming-webhook) to learn more about signing and verifying webhooks. |
### How to Test Serverless Functions
@@ -124,7 +123,8 @@ We'll use the querystring to pass the `dividend` and `divisor` to the function h
```bash
// request
-http://localhost:8911/divide?dividend=10&divisor=2
+http://localhost:8911/divide?dividend=10 &
+divisor=2
```
If the function can successfully divide the two numbers, the function returns a body payload back in the response with a [HTTP 200 Success](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200) status:
@@ -197,10 +197,10 @@ To test a serverless function, you'll work with the test script associated with
```bash
api
├── src
-│ ├── functions
-│ │ ├── divide
-│ │ │ ├── divide.ts
-│ │ │ ├── divide.test.ts
+│ ├── functions
+│ │ ├── divide
+│ │ │ ├── divide.ts
+│ │ │ ├── divide.test.ts
```
The setup steps are to:
@@ -317,11 +317,11 @@ First, let's create a fixture for the `divide` function alongside your function
```bash
api
├── src
-│ ├── functions
-│ │ ├── divide
-│ │ │ ├── divide.ts
-│ │ │ ├── divide.test.ts
-│ │ │ ├── divide.fixtures.ts // <-- your fixture
+│ ├── functions
+│ │ ├── divide
+│ │ │ ├── divide.ts
+│ │ │ ├── divide.test.ts
+│ │ │ ├── divide.fixtures.ts // your fixture < --
```
Let's define a fixture for a new test case: when the function is invoked, but it is missing a divisor:
@@ -414,12 +414,11 @@ yarn rw generate function updateOrderStatus
```bash
api
├── src
-│ ├── functions
-│ │ ├── updateOrderStatus
-│ │ │ ├── updateOrderStatus.ts
-│ │ │ ├── updateOrderStatus.scenarios.ts
-│ │ │ ├── updateOrderStatus.test.ts
-
+│ ├── functions
+│ │ ├── updateOrderStatus
+│ │ │ ├── updateOrderStatus.ts
+│ │ │ ├── updateOrderStatus.scenarios.ts
+│ │ │ ├── updateOrderStatus.test.ts
```
The `updateOrderStatus` webhook will expect:
@@ -435,7 +434,11 @@ The `updateOrderStatus` webhook will expect:
```tsx
import type { APIGatewayEvent } from 'aws-lambda'
-import { verifyEvent, VerifyOptions, WebhookVerificationError } from '@redwoodjs/api/webhooks'
+import {
+ verifyEvent,
+ VerifyOptions,
+ WebhookVerificationError,
+} from '@redwoodjs/api/webhooks'
import { db } from 'src/lib/db'
export const handler = async (event: APIGatewayEvent) => {
@@ -475,7 +478,9 @@ export const handler = async (event: APIGatewayEvent) => {
// updated the order with the new status
// using the trackingNumber provided
const order = await db.order.update({
- where: { trackingNumber_status: { trackingNumber, status: currentOrderStatus } },
+ where: {
+ trackingNumber_status: { trackingNumber, status: currentOrderStatus },
+ },
data: { status: status },
})
@@ -589,21 +594,27 @@ For brevity we didn't test that the order's status wasn't changed, but that coul
:::
```jsx
-scenario('with an invalid signature header, the webhook is unauthorized', async (scenario) => {
- const order = scenario.order.placed
-
- const payload = { trackingNumber: order.trackingNumber, status: 'DELIVERED' }
- const event = mockSignedWebhook({
- payload,
- signatureType: 'sha256Verifier',
- signatureHeader: 'X-Webhook-Signature-Invalid',
- secret: 'MY-VOICE-IS-MY-PASSPORT-VERIFY-ME',
- })
+scenario(
+ 'with an invalid signature header, the webhook is unauthorized',
+ async (scenario) => {
+ const order = scenario.order.placed
+
+ const payload = {
+ trackingNumber: order.trackingNumber,
+ status: 'DELIVERED',
+ }
+ const event = mockSignedWebhook({
+ payload,
+ signatureType: 'sha256Verifier',
+ signatureHeader: 'X-Webhook-Signature-Invalid',
+ secret: 'MY-VOICE-IS-MY-PASSPORT-VERIFY-ME',
+ })
- const result = await handler(event)
+ const result = await handler(event)
- expect(result.statusCode).toBe(401)
-})
+ expect(result.statusCode).toBe(401)
+ }
+)
```
Next, we test what happens if the event payload is signed, but with a different secret than it expects; that is it was signed using the wrong secret (`MY-NAME-IS-WERNER-BRANDES-VERIFY-ME` and not `MY-VOICE-IS-MY-PASSPORT-VERIFY-ME`).
@@ -611,44 +622,53 @@ Next, we test what happens if the event payload is signed, but with a different
Again, we expect as 401 Unauthorized response.
```jsx
-scenario('with the wrong webhook secret the webhook is unauthorized', async (scenario) => {
- const order = scenario.order.placed
-
- const payload = { trackingNumber: order.trackingNumber, status: 'DELIVERED' }
- const event = mockSignedWebhook({
- payload,
- signatureType: 'sha256Verifier',
- signatureHeader: 'X-Webhook-Signature',
- secret: 'MY-NAME-IS-WERNER-BRANDES-VERIFY-ME',
- })
+scenario(
+ 'with the wrong webhook secret the webhook is unauthorized',
+ async (scenario) => {
+ const order = scenario.order.placed
+
+ const payload = {
+ trackingNumber: order.trackingNumber,
+ status: 'DELIVERED',
+ }
+ const event = mockSignedWebhook({
+ payload,
+ signatureType: 'sha256Verifier',
+ signatureHeader: 'X-Webhook-Signature',
+ secret: 'MY-NAME-IS-WERNER-BRANDES-VERIFY-ME',
+ })
- const result = await handler(event)
+ const result = await handler(event)
- expect(result.statusCode).toBe(401)
-})
+ expect(result.statusCode).toBe(401)
+ }
+)
```
Next, what happens if the order cannot be found? We'll try a tracking number that doesn't exist (that is we did not create it in our scenario order data):
```jsx
-scenario('when the tracking number cannot be found, returns an error', async (scenario) => {
- const order = scenario.order.placed
-
- const payload = { trackingNumber: '1Z-DOES-NOT-EXIST', status: 'DELIVERED' }
- const event = mockSignedWebhook({
- payload,
- signatureType: 'sha256Verifier',
- signatureHeader: 'X-Webhook-Signature',
- secret: 'MY-VOICE-IS-MY-PASSPORT-VERIFY-ME',
- })
+scenario(
+ 'when the tracking number cannot be found, returns an error',
+ async (scenario) => {
+ const order = scenario.order.placed
+
+ const payload = { trackingNumber: '1Z-DOES-NOT-EXIST', status: 'DELIVERED' }
+ const event = mockSignedWebhook({
+ payload,
+ signatureType: 'sha256Verifier',
+ signatureHeader: 'X-Webhook-Signature',
+ secret: 'MY-VOICE-IS-MY-PASSPORT-VERIFY-ME',
+ })
- const result = await handler(event)
+ const result = await handler(event)
- const body = JSON.parse(result.body)
+ const body = JSON.parse(result.body)
- expect(result.statusCode).toBe(500)
- expect(body).toHaveProperty('error')
-})
+ expect(result.statusCode).toBe(500)
+ expect(body).toHaveProperty('error')
+ }
+)
```
Last, we want to test a business rule that says you cannot update an order to be delivered if it already is delivered
@@ -735,7 +755,7 @@ Serverless functions can use the same user-authentication strategy used by Graph
:::tip
- If you need to protect an endpoint via authentication that isn't user-based, you should consider using [Webhooks](webhooks.md) with a signed payload and verifier.
+If you need to protect an endpoint via authentication that isn't user-based, you should consider using [Webhooks](webhooks.md) with a signed payload and verifier.
:::
@@ -778,7 +798,7 @@ const myHandler = async (event: APIGatewayEvent, context: Context) => {
data: 'myHandler function',
}),
}
- // highlight-start
+ // highlight-start
} else {
logger.error('Access to myHandler was denied')
@@ -824,7 +844,6 @@ auth-provider: supabase
Content-Type: application/json
```
-
### Other security considerations
In addition to securing your serverless functions, you may consider logging, rate limiting and whitelisting as ways to protect your functions from abuse or misuse.
@@ -863,7 +882,11 @@ Because the `event` passed to the function handler contains the request's IP add
```jsx
const ipAddress = ({ event }) => {
- return event?.headers?.['client-ip'] || event?.requestContext?.identity?.sourceIp || 'localhost'
+ return (
+ event?.headers?.['client-ip'] ||
+ event?.requestContext?.identity?.sourceIp ||
+ 'localhost'
+ )
}
```
diff --git a/docs/docs/services.md b/docs/docs/services.md
index 52bb916f0a8a..0b6c49ef88e0 100644
--- a/docs/docs/services.md
+++ b/docs/docs/services.md
@@ -47,21 +47,34 @@ Finally, Services can also be called from [serverless functions](serverless-func
Redwood includes a feature we call Service Validations. These simplify an extremely common task: making sure that incoming data is formatted properly before continuing. These validations are meant to be included at the start of your Service function and will throw an error if conditions are not met:
```jsx
-import { validate, validateWith, validateWithSync, validateUniqueness } from '@redwoodjs/api'
+import {
+ validate,
+ validateWith,
+ validateWithSync,
+ validateUniqueness,
+} from '@redwoodjs/api'
export const createUser = async ({ input }) => {
validate(input.firstName, 'First name', {
presence: true,
- exclusion: { in: ['Admin', 'Owner'], message: 'That name is reserved, sorry!' },
- length: { min: 2, max: 255 }
+ exclusion: {
+ in: ['Admin', 'Owner'],
+ message: 'That name is reserved, sorry!',
+ },
+ length: { min: 2, max: 255 },
})
validateWithSync(() => {
- if (input.role === 'Manager' && !context.currentUser.roles.includes('admin')) {
+ if (
+ input.role === 'Manager' &&
+ !context.currentUser.roles.includes('admin')
+ ) {
throw 'Only Admins can create new Managers'
}
})
await validateWith(async () => {
- const inviteCount = await db.invites.count({ where: { userId: currentUser.id } })
+ const inviteCount = await db.invites.count({
+ where: { userId: currentUser.id },
+ })
if (inviteCount >= 10) {
throw 'You have already invited your max of 10 users'
}
@@ -75,7 +88,7 @@ export const createUser = async ({ input }) => {
> **What's the difference between Service Validations and Validator Directives?**
>
-> [Validator Directives](directives.md#validators) were added to Redwood in v0.37 and provide a way to validate whether data going through GraphQL is allowed based on the user that's currently requesting it (the user that is logged in). These directives control *access* to data, while Service Validators operate on a different level, outside of GraphQL, and make sure data is formatted properly before, most commonly, putting it into a database.
+> [Validator Directives](directives.md#validators) were added to Redwood in v0.37 and provide a way to validate whether data going through GraphQL is allowed based on the user that's currently requesting it (the user that is logged in). These directives control _access_ to data, while Service Validators operate on a different level, outside of GraphQL, and make sure data is formatted properly before, most commonly, putting it into a database.
>
> You could use these in combination to, for example, prevent a client from accessing the email addresses of any users that aren't themselves (Validator Directives) while also verifying that when creating a user, an email address is present, formatted correctly, and unique (Service Validations).
@@ -151,7 +164,7 @@ Please provide a valid email address
#### Multiple Validations
-You can provide multiple validations in the last argument object, some with custom messages and some without. If you include only *some* custom messages, make sure to use the 3-argument version as the ones without custom messages will need a variable name to include their messages:
+You can provide multiple validations in the last argument object, some with custom messages and some without. If you include only _some_ custom messages, make sure to use the 3-argument version as the ones without custom messages will need a variable name to include their messages:
```jsx
validate(input.name, 'Name', {
@@ -191,25 +204,25 @@ Opposite of the [presence](#presence) validator.
```jsx
validate(input.value, 'Value', {
- absence: true
+ absence: true,
})
```
##### Options
-* `allowEmptyString` will count an empty string as being absent (that is, `null`, `undefined` and `""` will pass this validation)
+- `allowEmptyString` will count an empty string as being absent (that is, `null`, `undefined` and `""` will pass this validation)
```jsx
validate(input.honeypot, 'Honeypot', {
- absence: { allowEmptyString: true }
+ absence: { allowEmptyString: true },
})
```
-* `message`: a message to be shown if the validation fails
+- `message`: a message to be shown if the validation fails
```jsx
validate(input.value, {
- absence: { message: 'Value must be absent' }
+ absence: { message: 'Value must be absent' },
})
```
@@ -219,25 +232,25 @@ Requires that the passed value be `true`, or within an array of allowed values t
```jsx
validate(input.terms, 'Terms of Service', {
- acceptance: true
+ acceptance: true,
})
```
##### Options
-* `in`: an array of values that, if any match, will pass the validation
+- `in`: an array of values that, if any match, will pass the validation
```jsx
validate(input.terms, 'Terms of Service', {
- acceptance: { in: [true, 'true', 1, '1'] }
+ acceptance: { in: [true, 'true', 1, '1'] },
})
```
-* `message`: a custom message if validation fails
+- `message`: a custom message if validation fails
```jsx
validate(input.terms, {
- acceptance: { message: 'Please accept the Terms of Service' }
+ acceptance: { message: 'Please accept the Terms of Service' },
})
```
@@ -245,23 +258,23 @@ validate(input.terms, {
Requires that the value be formatted like an email address by comparing against a regular expression. The regex is extremely lax: `/^[^@\s]+@[^.\s]+\.[^\s]+$/` This says that the value:
-* Must start with one or more characters that aren't a whitespace or literal `@`
-* Followed by a `@`
-* Followed by one or more characters that aren't a whitespace or literal `.`
-* Followed by a `.`
-* Ending with one or more characters that aren't whitespace
+- Must start with one or more characters that aren't a whitespace or literal `@`
+- Followed by a `@`
+- Followed by one or more characters that aren't a whitespace or literal `.`
+- Followed by a `.`
+- Ending with one or more characters that aren't whitespace
Since the [official email regex](http://www.ex-parrot.com/~pdw/Mail-RFC822-Address.html) is around 6,300 characters long, we though this one was good enough. If you have a different, preferred email validation regular expression, use the [format](#format) validation.
```jsx
validate(input.email, 'Email', {
- email: true
+ email: true,
})
```
##### Options
-* `message`: a custom message if validation fails
+- `message`: a custom message if validation fails
```jsx
validate(input.email, {
@@ -271,33 +284,33 @@ validate(input.email, {
#### Exclusion
-Requires that the given value *not* equal to any in a list of given values. Opposite of the [inclusion](#inclusion) validation.
+Requires that the given value _not_ equal to any in a list of given values. Opposite of the [inclusion](#inclusion) validation.
```jsx
validate(input.name, 'Name', {
- exclusion: ['Admin', 'Owner']
+ exclusion: ['Admin', 'Owner'],
})
```
##### Options
-* `in`: the list of values that cannot be used
-* `caseSensitive`: toggles case sensitivity; default: `true`
+- `in`: the list of values that cannot be used
+- `caseSensitive`: toggles case sensitivity; default: `true`
```jsx
validate(input.name, 'Name', {
- exclusion: { in: ['Admin', 'Owner'] }
+ exclusion: { in: ['Admin', 'Owner'] },
})
```
-* `message`: a custom error message if validation fails
+- `message`: a custom error message if validation fails
```jsx
validate(input.name, {
exclusion: {
in: ['Admin', 'Owner'],
- message: 'That name is reserved, try another'
- }
+ message: 'That name is reserved, try another',
+ },
})
```
@@ -307,61 +320,60 @@ Requires that the value match a given regular expression.
```jsx
validate(input.usPhone, 'US Phone Number', {
- format: /^[0-9-]{10,12}$/
+ format: /^[0-9-]{10,12}$/,
})
```
##### Options
-* `pattern`: the regular expression to use
+- `pattern`: the regular expression to use
```jsx
validate(input.usPhone, 'US Phone Number', {
- format: { pattern: /^[0-9-]{10,12}$/ }
+ format: { pattern: /^[0-9-]{10,12}$/ },
})
```
-* `message`: a custom error message if validation fails
-
+- `message`: a custom error message if validation fails
```jsx
validate(input.usPhone, {
format: {
pattern: /^[0-9-]{10,12}$/,
- message: 'Can only contain numbers and dashes'
- }
+ message: 'Can only contain numbers and dashes',
+ },
})
```
#### Inclusion
-Requires that the given value *is* equal to one in a list of given values. Opposite of the [exclusion](#exclusion) validation.
+Requires that the given value _is_ equal to one in a list of given values. Opposite of the [exclusion](#exclusion) validation.
```jsx
validate(input.role, 'Role', {
- inclusion: ['Guest', 'Member', 'Manager']
+ inclusion: ['Guest', 'Member', 'Manager'],
})
```
##### Options
-* `in`: the list of values that can be used
-* `caseSensitive`: toggles case sensitivity; default: `true`
+- `in`: the list of values that can be used
+- `caseSensitive`: toggles case sensitivity; default: `true`
```jsx
validate(input.role, 'Role', {
- inclusion: { in: ['Guest', 'Member', 'Manager'] }
+ inclusion: { in: ['Guest', 'Member', 'Manager'] },
})
```
-* `message`: a custom error message if validation fails
+- `message`: a custom error message if validation fails
```jsx
validate(input.role, 'Role', {
inclusion: {
- in: ['Guest', 'Member', 'Manager'] ,
- message: 'Please select a proper role'
- }
+ in: ['Guest', 'Member', 'Manager'],
+ message: 'Please select a proper role',
+ },
})
```
@@ -371,49 +383,53 @@ Requires that the value meet one or more of a number of string length validation
```jsx
validate(input.answer, 'Answer', {
- length: { min: 6, max: 200 }
+ length: { min: 6, max: 200 },
})
```
##### Options
-* `min`: must be at least this number of characters long
+- `min`: must be at least this number of characters long
```jsx
validate(input.name, 'Name', {
- length: { min: 2 }
+ length: { min: 2 },
})
```
-* `max`: must be no more than this number of characters long
+- `max`: must be no more than this number of characters long
```jsx
validate(input.company, 'Company', {
- length: { max: 255 }
+ length: { max: 255 },
})
```
-* `equal`: must be exactly this number of characters long
+- `equal`: must be exactly this number of characters long
```jsx
validate(input.pin, 'PIN', {
- length: { equal: 4 }
+ length: { equal: 4 },
})
```
-* `between`: convenience syntax for defining min and max as an array
+- `between`: convenience syntax for defining min and max as an array
```jsx
validate(input.title, 'Title', {
- length: { between: [2, 255] }
+ length: { between: [2, 255] },
})
```
-* `message`: a custom message if validation fails. Can use length options as string interpolations in the message itself, including `name` which is the name of the field provided in the second argument
+- `message`: a custom message if validation fails. Can use length options as string interpolations in the message itself, including `name` which is the name of the field provided in the second argument
```jsx
validate(input.title, 'Title', {
- length: { min: 2, max: 255, message: '${name} must be between ${min} and ${max} characters' }
+ length: {
+ min: 2,
+ max: 255,
+ message: '${name} must be between ${min} and ${max} characters',
+ },
})
```
@@ -425,105 +441,108 @@ The awesomely-named Numericality Validation requires that the value passed meet
```jsx
validate(input.year, 'Year', {
- numericality: { greaterThan: 1900, lessThanOrEqual: 2021 }
+ numericality: { greaterThan: 1900, lessThanOrEqual: 2021 },
})
```
##### Options
-* `integer`: the number must be an integer
+- `integer`: the number must be an integer
```jsx
validate(input.age, 'Age', {
- numericality: { integer: true }
+ numericality: { integer: true },
})
```
-* `lessThan`: the number must be less than the given value
+- `lessThan`: the number must be less than the given value
```jsx
validate(input.temp, 'Temperature', {
- numericality: { lessThan: 100 }
+ numericality: { lessThan: 100 },
})
```
-* `lessThanOrEqual`: the number must be less than or equal to the given value
+- `lessThanOrEqual`: the number must be less than or equal to the given value
```jsx
validate(input.temp, 'Temperature', {
- numericality: { lessThanOrEqual: 100 }
+ numericality: { lessThanOrEqual: 100 },
})
```
-* `greaterThan`: the number must be greater than the given value
+- `greaterThan`: the number must be greater than the given value
```jsx
validate(input.temp, 'Temperature', {
- numericality: { greaterThan: 32 }
+ numericality: { greaterThan: 32 },
})
```
-* `greaterThanOrEqual`: the number must be greater than or equal to the given number
+- `greaterThanOrEqual`: the number must be greater than or equal to the given number
```jsx
validate(input.temp, 'Temperature', {
- numericality: { greaterThanOrEqual: 32 }
+ numericality: { greaterThanOrEqual: 32 },
})
```
-* `equal`: the number must be equal to the given number
+- `equal`: the number must be equal to the given number
```jsx
validate(input.guess, 'Guess', {
- numericality: { equal: 6 }
+ numericality: { equal: 6 },
})
```
-* `otherThan`: the number must not be equal to the given number
+- `otherThan`: the number must not be equal to the given number
```jsx
validate(input.floor, 'Floor', {
- numericality: { otherThan: 13 }
+ numericality: { otherThan: 13 },
})
```
-* `even`: the number must be even
+- `even`: the number must be even
```jsx
validate(input.skip, 'Skip', {
- numericality: { even: true }
+ numericality: { even: true },
})
```
-* `odd`: the number must be odd
+- `odd`: the number must be odd
```jsx
validate(input.zenGarden, 'Zen Garden', {
- numericality: { odd: true }
+ numericality: { odd: true },
})
```
-* `positive`: the number must be positive (greater than 0)
+- `positive`: the number must be positive (greater than 0)
```jsx
validate(input.balance, 'Balance', {
- numericality: { positive: true }
+ numericality: { positive: true },
})
```
-* `negative`: the number must be negative (less than 0)
+- `negative`: the number must be negative (less than 0)
```jsx
validate(input.debt, 'Debt', {
- numericality: { negative: true }
+ numericality: { negative: true },
})
```
-* `message`: a custom message if validation fails. Some options can be used in string interpolation: `lessThan`, `lessThanOrEqual`, `greaterThan`, `greaterThanOrEqual`, `equal`, and `otherThan`
+- `message`: a custom message if validation fails. Some options can be used in string interpolation: `lessThan`, `lessThanOrEqual`, `greaterThan`, `greaterThanOrEqual`, `equal`, and `otherThan`
```jsx
validate(input.floor, 'Floor', {
- numericality: { otherThan: 13, message: 'You cannot go to floor ${otherThan}' }
+ numericality: {
+ otherThan: 13,
+ message: 'You cannot go to floor ${otherThan}',
+ },
})
```
@@ -536,50 +555,50 @@ Opposite of the [absence](#absence) validator.
```jsx
validate(input.value, 'Value', {
- presence: true
+ presence: true,
})
```
##### Options
-* `allowNull`: whether or not to allow `null` to be considered present (default is `false`)
+- `allowNull`: whether or not to allow `null` to be considered present (default is `false`)
```jsx
validate(input.value, 'Value', {
- presence: { allowNull: true }
+ presence: { allowNull: true },
})
// `null` passes
// `undefined` fails
// "" passes
```
-* `allowUndefined`: whether or not to allow `undefined` to be considered present (default is `false`)
+- `allowUndefined`: whether or not to allow `undefined` to be considered present (default is `false`)
```jsx
validate(input.value, 'Value', {
- presence: { allowUndefined: true }
+ presence: { allowUndefined: true },
})
// `null` fails
// `undefined` passes
// "" passes
```
-* `allowEmptyString`: whether or not to allow an empty string `""` to be considered present (default is `true`)
+- `allowEmptyString`: whether or not to allow an empty string `""` to be considered present (default is `true`)
```jsx
validate(input.value, 'Value', {
- presence: { allowEmptyString: false }
+ presence: { allowEmptyString: false },
})
// `null` fails
// `undefined` fails
// "" fails
```
-* `message`: a message to be shown if the validation fails
+- `message`: a message to be shown if the validation fails
```jsx
validate(input.lastName, {
- presence: { allowEmptyString: false, message: "Can't leave last name empty" }
+ presence: { allowEmptyString: false, message: "Can't leave last name empty" },
})
```
@@ -595,14 +614,14 @@ validate(input.value, 'Value', {
if (isInvalid) {
throw new Error('Value is invalid')
}
- }
- }
+ },
+ },
})
```
##### Options
-* `message`: a custom error message if validation fails
+- `message`: a custom error message if validation fails
```jsx
validate(input.value, 'Value', {
@@ -612,10 +631,11 @@ validate(input.value, 'Value', {
throw new Error('Value is invalid')
}
},
- message: 'Please specify a different value'
- }
+ message: 'Please specify a different value',
+ },
})
```
+
### validateWithSync()
`validateWithSync()` is simply given a function to execute. This function should throw with a message if there is a problem, otherwise do nothing.
@@ -644,8 +664,8 @@ The same behavior as `validateWithSync()` but works with Promises. Remember to `
```jsx
await validateWith(async () => {
- if (await db.products.count() >= 100) {
- throw "There can only be a maximum of 100 products in your store"
+ if ((await db.products.count()) >= 100) {
+ throw 'There can only be a maximum of 100 products in your store'
}
})
```
@@ -703,7 +723,8 @@ You can provide a custom message if the validation failed with the optional thir
```jsx
const createUser = (input) => {
- return validateUniqueness('user',
+ return validateUniqueness(
+ 'user',
{ email: input.email },
{ message: 'Your email is already in use' },
(db) => db.user.create({ data: input })
@@ -712,14 +733,13 @@ const createUser = (input) => {
```
You can provide the PrismaClient to be used for the transaction and callback.
+
```jsx
import { db } from 'src/lib/db'
const createUser = (input) => {
- return validateUniqueness('user',
- { email: input.email },
- { db },
- (db) => db.user.create({ data: input })
+ return validateUniqueness('user', { email: input.email }, { db }, (db) =>
+ db.user.create({ data: input })
)
}
```
@@ -749,12 +769,16 @@ Sometimes we may only want to check uniqueness against a subset of records, say
```jsx
const createPost = (input) => {
- return validateUniqueness('post', {
- title: input.title,
- $scope: { userId: context.currentUser.id }
- }, (db) => {
- return db.user.create({ data: input })
- })
+ return validateUniqueness(
+ 'post',
+ {
+ title: input.title,
+ $scope: { userId: context.currentUser.id },
+ },
+ (db) => {
+ return db.user.create({ data: input })
+ }
+ )
}
```
@@ -795,7 +819,12 @@ export const updatePost = async ({ id, input }) => {
where: { id },
})
// highlight-next-line
- await cacheClient.MSET(`post-${id}`, JSON.stringify(post), `blogpost-${id}`, JSON.stringify(post))
+ await cacheClient.MSET(
+ `post-${id}`,
+ JSON.stringify(post),
+ `blogpost-${id}`,
+ JSON.stringify(post)
+ )
return post
}
@@ -827,10 +856,10 @@ Okay, let's use the product's database ID as the key: `41443`. It's definitely g
What if we add a "type" into the cache key, so we know what type of thing we're caching: `product-41442`. Now we're getting somewhere. Users will have a cache key `user-41442` and the two won't clash. But what happens if you change some data about that product, like the description? Remember that we can only get an existing key/value, or create a key/value in the cache, we can't update an existing key. How we can encapsulate the "knowledge" that a product's data has changed into the cache key?
-One solution would be to put all of the data that we care about changing into the key, like: `product-41442-${description}`. The problem here is that keys can only be so long (in Memcached it's 250 bytes). Another option could be to hash the entire product object and use that as the key (this can encompass the `product` part of the key as well as the ID itself, since *any* data in the object being different will result in a new hash):
+One solution would be to put all of the data that we care about changing into the key, like: `product-41442-${description}`. The problem here is that keys can only be so long (in Memcached it's 250 bytes). Another option could be to hash the entire product object and use that as the key (this can encompass the `product` part of the key as well as the ID itself, since _any_ data in the object being different will result in a new hash):
```js
-import { md5 } from "blueimp-md5"
+import { md5 } from 'blueimp-md5'
cache(md5(JSON.stringify(product)), () => {
// ...
@@ -850,9 +879,10 @@ cache(product, () => {
// ...
})
```
+
:::
-One drawback to this key is in potentially responding to *too many* data changes, even ones we don't care about caching. Imagine that a product has a `views` field that tracks how many times it has been viewed in the browser. This number will be changing all the time, but if we don't display that count to the user then we're constantly re-creating the cache for the product even though no data the user will see is changing. There's no way to tell Prisma "set the `updatedAt` when the record changes, but not if the `views` column changes." This cache key is too variable. One solution would be to move the `views` column to another table with a `productId` pointing back to this record. Now the `product` is back to just containing data we care about caching.
+One drawback to this key is in potentially responding to _too many_ data changes, even ones we don't care about caching. Imagine that a product has a `views` field that tracks how many times it has been viewed in the browser. This number will be changing all the time, but if we don't display that count to the user then we're constantly re-creating the cache for the product even though no data the user will see is changing. There's no way to tell Prisma "set the `updatedAt` when the record changes, but not if the `views` column changes." This cache key is too variable. One solution would be to move the `views` column to another table with a `productId` pointing back to this record. Now the `product` is back to just containing data we care about caching.
What if you want to expire a cache regardless of whether the data itself has changed? Maybe you make a UI change where you now show a product's SKU on the page where you didn't before. You weren't previously selecting the `sku` field out of the database, and so it hasn't been cached. But now that you're showing it you'll need to add it the list of fields to return from the service. One solution would be forcibly update all of the `updatedAt` fields in the database. But a) Prisma won't easily let you do this since it think it controls that column, and b) every product is going to appear to have been edited at the same time, when in fact nothing changed—you just needed to bust the cache.
@@ -864,16 +894,16 @@ And this key is our final form: a unique, but flexible key that allows us to exp
One more case: what if the underlying `Product` model itself changes, adding a new field, for example? Each product will now have new data, but no changes will occur to `updatedAt` as a result of adding this column. There are a couple things you could do here:
-* Increment the version on the key, if you have one: `v1` => `v2`
-* "Touch" all of the Product records in a script, forcing them to have their `updatedAt` timestamp changed
-* Incorporate a hash of all the keys of a `product` into the cache key
+- Increment the version on the key, if you have one: `v1` => `v2`
+- "Touch" all of the Product records in a script, forcing them to have their `updatedAt` timestamp changed
+- Incorporate a hash of all the keys of a `product` into the cache key
How does that last one work? We get a list of all the keys and then apply a hashing algorithm like MD5 to get a string that's unique based on that list of database columns. Then if one is ever added or removed, the hash will change, which will change the key, which will bust the cache:
```javascript
const product = db.product.findUnique({ where: { id } })
const columns = Object.keys(product) // ['id', 'name', 'sku', ...]
-const hash = md5(columns.join(',')) // "e4d7f1b4ed2e42d15898f4b27b019da4"
+const hash = md5(columns.join(',')) // "e4d7f1b4ed2e42d15898f4b27b019da4"
cache(`v1-product-${hash}-${id}-${updatedAt}`, () => {
// ...
@@ -889,16 +919,20 @@ Note that this has the side effect of having to select at least one record from
You can skirt these issues about what data is changing and what to include or not include in the key by just setting an expiration time on this cache entry. You may decide that if a change is made to a product, it's okay if users don't see the change for, say, an hour. In this case just set the expiration time to 3600 seconds and it will automatically be re-built, whether something changed in the record or not:
```js
-cache(`product-${id}`, () => {
- // ...
-}, { expires: 3600 })
+cache(
+ `product-${id}`,
+ () => {
+ // ...
+ },
+ { expires: 3600 }
+)
```
-This leads to your product cache being rebuilt every hour, even though you haven't made any changes that are of consequence to the user. But that may be we worth the tradeoff versus rebuilding the cache when *no* useful data has changed (like the `views` column being updated).
+This leads to your product cache being rebuilt every hour, even though you haven't made any changes that are of consequence to the user. But that may be we worth the tradeoff versus rebuilding the cache when _no_ useful data has changed (like the `views` column being updated).
#### Global Cache Key Prefix
-Just like the `v1` we added to the `product` cache key above, you can globally prefix a string to *all* of your cache keys:
+Just like the `v1` we added to the `product` cache key above, you can globally prefix a string to _all_ of your cache keys:
```js title="api/src/lib/cache.js"
export const { cache, cacheFindMany } = createCache(client, {
@@ -911,7 +945,7 @@ export const { cache, cacheFindMany } = createCache(client, {
This would turn a key like `posts-123` into `alpha-posts-123` before giving it to the cache client. If you prefixed with `v1` in the individual cache key, you'd now have `alpha-v1-posts-123`.
-This gives you a nuclear option to invalidate all cache keys globally in your app. Let's say you launched a new redesign, or other visual change to your site where you may be showing more or less data from your GraphQL queries. If your data was purely based on the DB data (like `id` and `updatedAt`) there would be no way to refresh all of these keys without changing each and every cache key manually in every service, or by manually updating *all* `updatedAt` timestamps in the database. This gives you a fallback to refreshing all data at once.
+This gives you a nuclear option to invalidate all cache keys globally in your app. Let's say you launched a new redesign, or other visual change to your site where you may be showing more or less data from your GraphQL queries. If your data was purely based on the DB data (like `id` and `updatedAt`) there would be no way to refresh all of these keys without changing each and every cache key manually in every service, or by manually updating _all_ `updatedAt` timestamps in the database. This gives you a fallback to refreshing all data at once.
#### Caching User-specific Data
@@ -923,9 +957,9 @@ cache(`recommended-${context.currentUser.id}`, () => {
})
```
-If every page the user visits has a different list of recommended products for every page (meaning that the full computation will need to run at least once, before it's cached) then creating this cache may not be worth it: how often does the user revisit the same product page more than once? Conversely, if you show the *same* recommended products on every page then this cache would definitely improve the user's experience.
+If every page the user visits has a different list of recommended products for every page (meaning that the full computation will need to run at least once, before it's cached) then creating this cache may not be worth it: how often does the user revisit the same product page more than once? Conversely, if you show the _same_ recommended products on every page then this cache would definitely improve the user's experience.
-The *key* to writing a good key (!) is to think carefully about the circumstances in which the key needs to expire, and include those bits of information into the key string/array. Adding caching can lead to weird bugs you don't expect, but in these cases the root cause will usually be the cache key not containing enough bits of information to expire it correctly. When in doubt, restart the app with the cache server (memcached or redis) disabled and see if the same behavior is still present. If not, the cache key is the culprit!
+The _key_ to writing a good key (!) is to think carefully about the circumstances in which the key needs to expire, and include those bits of information into the key string/array. Adding caching can lead to weird bugs you don't expect, but in these cases the root cause will usually be the cache key not containing enough bits of information to expire it correctly. When in doubt, restart the app with the cache server (memcached or redis) disabled and see if the same behavior is still present. If not, the cache key is the culprit!
### Setup
@@ -981,7 +1015,7 @@ The second usage of the logger argument:
```js
export const { cache, cacheFindMany } = createCache(client, {
logger,
- timeout: 500
+ timeout: 500,
})
```
@@ -991,10 +1025,10 @@ is passing it to Redwood's own service cache code, so that it can log cache hits
There are several options you can pass to the `createCache()` call:
-* `logger`: an instance of the Redwood logger. Defaults to `null`, but if you want any feedback about what the cache is doing, make sure to set this!
-* `timeout`: how long to wait for the cache server to respond during a get/set before giving up and just executing the function containing what you want to cache and returning the result directly. Defaults to `500` milliseconds.
-* `prefix`: a global cache key prefix. Defaults to `null`.
-* `fields`: an object that maps the model field names for the `id` and `updatedAt` fields if your database has another name for them. For example: `fields: { id: 'post_id', updatedAt: 'updated_at' }`. Even if only one of your names is different, you need to provide both properties to this option. Defaults to `{ id: 'id', updatedAt: 'updatedAt' }`
+- `logger`: an instance of the Redwood logger. Defaults to `null`, but if you want any feedback about what the cache is doing, make sure to set this!
+- `timeout`: how long to wait for the cache server to respond during a get/set before giving up and just executing the function containing what you want to cache and returning the result directly. Defaults to `500` milliseconds.
+- `prefix`: a global cache key prefix. Defaults to `null`.
+- `fields`: an object that maps the model field names for the `id` and `updatedAt` fields if your database has another name for them. For example: `fields: { id: 'post_id', updatedAt: 'updated_at' }`. Even if only one of your names is different, you need to provide both properties to this option. Defaults to `{ id: 'id', updatedAt: 'updatedAt' }`
### `cache()`
@@ -1010,9 +1044,13 @@ const post = ({ id }) => {
// cache for 1 hour
const post = ({ id }) => {
- return cache(`posts`, () => {
- return db.post.findMany()
- }, { expires: 3600 })
+ return cache(
+ `posts`,
+ () => {
+ return db.post.findMany()
+ },
+ { expires: 3600 }
+ )
}
```
@@ -1028,7 +1066,7 @@ const post = ({ id }) => {
// or
const post = ({ id }) => {
- return cache(['posts', id, updatedAt.getTime()], () => {
+ return cache(['posts', id, updatedAt.getTime()], () => {
return db.post.findMany()
})
}
@@ -1042,7 +1080,7 @@ const post = ({ id }) => {
### `cacheFindMany()`
-Use this function if you want to cache the results of a `findMany()` call from Prisma, but only until one or more of the records in the set is updated. This is sort of a best of both worlds cache scenario where you can cache as much data as possible, but also expire and re-cache as soon as any piece of it changes, without going through every record manually to see if it's changed: whenever *any* record changes the cache will be discarded.
+Use this function if you want to cache the results of a `findMany()` call from Prisma, but only until one or more of the records in the set is updated. This is sort of a best of both worlds cache scenario where you can cache as much data as possible, but also expire and re-cache as soon as any piece of it changes, without going through every record manually to see if it's changed: whenever _any_ record changes the cache will be discarded.
This function will always execute a `findFirst()` query to get the latest record that's changed, then use its `id` and `updatedAt` timestamp as the cache key for the full query. This means you'll always incur the overhead of a single DB call, but not the bigger `findMany()` unless something has changed. Note you still need to include a cache key prefix:
@@ -1057,7 +1095,7 @@ The above is the simplest usage example. If you need to pass a `where`, or any o
```js
const post = ({ id }) => {
return cacheFindMany(`users`, db.user, {
- conditions: { where: { roles: 'admin' } }
+ conditions: { where: { roles: 'admin' } },
})
}
```
@@ -1080,12 +1118,10 @@ If you also want to pass an `expires` option, do it in the same object as `condi
```js
const post = ({ id }) => {
- return cacheFindMany(
- `users`, db.user, {
- conditions: { where: { roles: 'admin' } },
- expires: 86400
- }
- )
+ return cacheFindMany(`users`, db.user, {
+ conditions: { where: { roles: 'admin' } },
+ expires: 86400,
+ })
}
```
@@ -1122,15 +1158,15 @@ Scenarios like this are what people are talking about when they say that caching
:::
-
### Testing what you cache
+
We wouldn't just give you all of these caching APIs and not show you how to test it right? You'll find all the details in the [Caching section in the testing doc](testing.md#testing-caching).
### Creating Your Own Client
If Memcached or Redis don't serve your needs, you can create your own client adapter. In the Redwood codebase take a look at `packages/api/src/cache/clients` as a reference for writing your own. The interface is extremely simple:
-* Extend from the `BaseClient` class.
-* A constructor that takes whatever arguments you want, passing them through to the client's initialization code.
-* A `get()` function that accepts a `key` argument and returns the data from the cache if found, otherwise `null`. Note that in the Memcached and Redis clients the value returned is first run through `JSON.parse()` but if your cache client supports native JS objects then you wouldn't need to do this.
-* A `set()` function that accepts a string `key`, the `value` to be cached, and an optional `options` object containing at least an `expires` key. Note that `value` can be a real JavaScript objects at this point, but in Memcached and Redis the value is run through `JSON.stringify()` before being sent to the client library. You may or may not need to do the same thing, depending on what your cache client supports.
+- Extend from the `BaseClient` class.
+- A constructor that takes whatever arguments you want, passing them through to the client's initialization code.
+- A `get()` function that accepts a `key` argument and returns the data from the cache if found, otherwise `null`. Note that in the Memcached and Redis clients the value returned is first run through `JSON.parse()` but if your cache client supports native JS objects then you wouldn't need to do this.
+- A `set()` function that accepts a string `key`, the `value` to be cached, and an optional `options` object containing at least an `expires` key. Note that `value` can be a real JavaScript objects at this point, but in Memcached and Redis the value is run through `JSON.stringify()` before being sent to the client library. You may or may not need to do the same thing, depending on what your cache client supports.
diff --git a/docs/docs/storybook.md b/docs/docs/storybook.md
index 4a8c4274148b..280fe5382c54 100644
--- a/docs/docs/storybook.md
+++ b/docs/docs/storybook.md
@@ -28,12 +28,13 @@ yarn rw storybook
```
If this is your first time running Storybook:
- - The Redwood CLI will install Storybook, the framework package, and all related dependencies.
- - The Redwood CLI will create the following config files for you:
- - `web/.storybook/main.ts`
- - This is the primary [Storybook configuration file](https://storybook.js.org/docs/configure). Note that it references our framework package, [`storybook-framework-redwoodjs-vite`](https://www.npmjs.com/package/storybook-framework-redwoodjs-vite).
- - `web/.storybook/preview-body.html`
- - This is required to change the `id` of the root div to `redwood-app`, which is what the entry file used by Vite requires.
+
+- The Redwood CLI will install Storybook, the framework package, and all related dependencies.
+- The Redwood CLI will create the following config files for you:
+ - `web/.storybook/main.ts`
+ - This is the primary [Storybook configuration file](https://storybook.js.org/docs/configure). Note that it references our framework package, [`storybook-framework-redwoodjs-vite`](https://www.npmjs.com/package/storybook-framework-redwoodjs-vite).
+ - `web/.storybook/preview-body.html`
+ - This is required to change the `id` of the root div to `redwood-app`, which is what the entry file used by Vite requires.
Once Storybook is all set up, it'll spin up on localhost port `7910` and open your browser.
diff --git a/docs/docs/studio.md b/docs/docs/studio.md
index c3d23de026be..0b4d5d39686d 100644
--- a/docs/docs/studio.md
+++ b/docs/docs/studio.md
@@ -16,37 +16,42 @@ While [logging](https://redwoodjs.com/docs/logger) can show you some of these st
We hope Studio helps solve this problem with an observability tool that combines:
-* Tracing with OpenTelemetry (service and GraphQL)
+- Tracing with OpenTelemetry (service and GraphQL)
-* SQL statement logging
+- SQL statement logging
-* general metrics (how many invocations)
+- general metrics (how many invocations)
-* GraphiQL playground with impersonated authentication
+- GraphiQL playground with impersonated authentication
With Studio, it is easier to:
-* identify slow running SQL statements without reviewing captured log files
+- identify slow running SQL statements without reviewing captured log files
-* identify and improve N+1 queries by comparing before and after traces
+- identify and improve N+1 queries by comparing before and after traces
-* impersonate the user authentication headers in GraphiQL
+- impersonate the user authentication headers in GraphiQL
Redwood Studio is a command line tool which offers a web UI aimed at providing insights into your application via OpenTelemetry ingestion and other development conveniences like auth-impersonation within GraphiQL.
### Demo
+
### Setup
+
There is no setup needed to begin using the studio; simply execute the following command to start the studio at `localhost:4318`:
+
```bash
yarn rw studio
```
+
The first time you run this command it will likely install the studio package which may take a small amount of time.
#### OpenTelemetry
+
If you want studio to pick up telemetry from you app automatically please ensure you've setup opentelemetry. A guide on this can be found [here](https://community.redwoodjs.com/t/opentelemetry-support-experimental/4772)
### Features
@@ -103,20 +108,22 @@ Requires a `SUPABASE_JWT_SECRET` environment variable for JWT signing.
All settings for Studio are located in `redwood.toml`, which you can find at
the root of your Redwood project.
-* `[studio.graphiql.authImpersonation].*` – Used to gain access to GraphQL
+- `[studio.graphiql.authImpersonation].*` – Used to gain access to GraphQL
endpoints that require authentication. See section above on auth
impersonation for more details.
-* `[studio].basePort` – Studio's web front-end will run on this port (default:
+- `[studio].basePort` – Studio's web front-end will run on this port (default:
4318). It is also used to calculate the port for the mailer integration and
other things. Please choose a port that is not already in use, and that has a
few more free ports available next to it.
-### Database File
+### Database File
+
Studio stores the ingested telemetry to `studio/prisma.db` within the
`.redwood` folder. You should not need to touch this file other than if you
wish to delete it to erase any existing telemetry data.
## Availability
+
Along the release of Redwood v7, Studio has been rewritten and is available as
a stable version. Just run `yarn rw studio` to start it! Prior to RW v7 Studio
was available as an experimental feature. If you're still not using the stable
diff --git a/docs/docs/testing.md b/docs/docs/testing.md
index 7cbe86c4b07e..2b0302d5bcec 100644
--- a/docs/docs/testing.md
+++ b/docs/docs/testing.md
@@ -4,7 +4,7 @@ description: A comprehensive reference for testing your app
# Testing
-Testing. For some it's an essential part of their development workflow. For others it's something they know they *should* do, but for whatever reason it hasn't struck their fancy yet. For others still it's something they ignore completely, hoping the whole concept will go away. But tests are here to stay, and maybe Redwood can change some opinions about testing being awesome and fun.
+Testing. For some it's an essential part of their development workflow. For others it's something they know they _should_ do, but for whatever reason it hasn't struck their fancy yet. For others still it's something they ignore completely, hoping the whole concept will go away. But tests are here to stay, and maybe Redwood can change some opinions about testing being awesome and fun.
## Introduction to Testing
@@ -12,7 +12,7 @@ If you're already familiar with the ins and outs of testing and just want to kno
## Building a Test Runner
-The idea of testing is pretty simple: for each "unit" of code you write, you write additional code that exercises that unit and makes sure it works as expected. What's a "unit" of code? That's for you to decide: it could be an entire class, a single function, or even a single line! In general, the smaller the unit, the better. Your tests will stay fast and focused on just one thing, which makes them easy to update when you refactor. The important thing is that you start *somewhere* and codify your code's functionality in a repeatable, verifiable way.
+The idea of testing is pretty simple: for each "unit" of code you write, you write additional code that exercises that unit and makes sure it works as expected. What's a "unit" of code? That's for you to decide: it could be an entire class, a single function, or even a single line! In general, the smaller the unit, the better. Your tests will stay fast and focused on just one thing, which makes them easy to update when you refactor. The important thing is that you start _somewhere_ and codify your code's functionality in a repeatable, verifiable way.
Let's say we write a function that adds two numbers together:
@@ -36,7 +36,7 @@ if (add(1, 1) === 2) {
}
```
-Pretty simple, right? The secret is that this simple check *is the basis of all testing*. Yes, that's it. So no matter how convoluted and theoretical the discussions on testing get, just remember that at the end of the day you're testing whether a condition is true or false.
+Pretty simple, right? The secret is that this simple check _is the basis of all testing_. Yes, that's it. So no matter how convoluted and theoretical the discussions on testing get, just remember that at the end of the day you're testing whether a condition is true or false.
### Running a Test
@@ -52,23 +52,23 @@ You should see "pass" written to the output. To verify that our test is working
Let's get to some terminology:
-* The entire code block that checks the functionality of `add()` is what's considered a single **test**
-* The specific check that `add(1, 1) === 2` is known as an **assertion**
-* The `add()` function itself is the **subject** of the test, or the code that is **under test**
-* The value you expect to get (in our example, that's the number `2`) is sometimes called the **expected value**
-* The value you actually get (whatever the output of `add(1, 1)` is) is sometimes called the **actual** or **received value**
-* The file that contains the test is a **test file**
-* Multiple test files, all run together, is known as a **test suite**
-* You'll generally run your test files and suites with another piece of software. In Redwood that's Jest, and it's known as a **test runner**
-* The amount of code you have that is exercised by tests is referred to as **coverage** and is usually reported as a percentage. If every single line of code is touched as a result of running your test suite then you have 100% coverage!
+- The entire code block that checks the functionality of `add()` is what's considered a single **test**
+- The specific check that `add(1, 1) === 2` is known as an **assertion**
+- The `add()` function itself is the **subject** of the test, or the code that is **under test**
+- The value you expect to get (in our example, that's the number `2`) is sometimes called the **expected value**
+- The value you actually get (whatever the output of `add(1, 1)` is) is sometimes called the **actual** or **received value**
+- The file that contains the test is a **test file**
+- Multiple test files, all run together, is known as a **test suite**
+- You'll generally run your test files and suites with another piece of software. In Redwood that's Jest, and it's known as a **test runner**
+- The amount of code you have that is exercised by tests is referred to as **coverage** and is usually reported as a percentage. If every single line of code is touched as a result of running your test suite then you have 100% coverage!
This is the basic idea behind all the tests you'll write: when you add code, you'll add another piece of code that uses the first and verifies that the result is what you expect.
-Tests can also help drive new development. For example, what happens to our `add()` function if you leave out one of the arguments? We can drive these changes by writing a test of what we *want* to happen, and then modify the code that's being tested (the subject) to make it satisfy the assertion(s).
+Tests can also help drive new development. For example, what happens to our `add()` function if you leave out one of the arguments? We can drive these changes by writing a test of what we _want_ to happen, and then modify the code that's being tested (the subject) to make it satisfy the assertion(s).
### Expecting Errors
-So, what does happen if we leave off an argument when calling `add()`? Well, what do we *want* to happen? We'll answer that question by writing a test for what we expect. For this example let's have it throw an error. We'll write the test first that expects the error:
+So, what does happen if we leave off an argument when calling `add()`? Well, what do we _want_ to happen? We'll answer that question by writing a test for what we expect. For this example let's have it throw an error. We'll write the test first that expects the error:
```jsx
try {
@@ -82,17 +82,17 @@ try {
}
```
-This is interesting because we actually *expect* an error to be thrown, but we don't want that error to stop the test suite in it's tracks—we want the error to be raised, we just want to make sure it's exactly what we expect it to be! So we'll surround the code that's going to error in a try/catch block and inspect the error message. If it's what we want, then the test actually passes.
+This is interesting because we actually _expect_ an error to be thrown, but we don't want that error to stop the test suite in it's tracks—we want the error to be raised, we just want to make sure it's exactly what we expect it to be! So we'll surround the code that's going to error in a try/catch block and inspect the error message. If it's what we want, then the test actually passes.
-> Remember: we're testing for what we *want* to happen. Usually you think of errors as being "bad" but in this case we *want* the code to throw an error, so if it does, that's actually good! Raising an error passes the test, not raising the error (or raising the wrong error) is a failure.
+> Remember: we're testing for what we _want_ to happen. Usually you think of errors as being "bad" but in this case we _want_ the code to throw an error, so if it does, that's actually good! Raising an error passes the test, not raising the error (or raising the wrong error) is a failure.
Run this test and what happens? (If you previously made a change to `add()` to see the test fail, change it back now):
-Where did *that* come from? Well, our subject `add()` didn't raise any errors (Javascript doesn't care about the number of arguments passed to a function) and so it tried to add `1` to `undefined`, and that's Not A Number. We didn't think about that! Testing is already helping us catch edge cases.
+Where did _that_ come from? Well, our subject `add()` didn't raise any errors (Javascript doesn't care about the number of arguments passed to a function) and so it tried to add `1` to `undefined`, and that's Not A Number. We didn't think about that! Testing is already helping us catch edge cases.
-To respond properly to this case we'll make one slight modification: add another "fail" log message if the code somehow gets past the call to `add(1)` *without* throwing an error:
+To respond properly to this case we'll make one slight modification: add another "fail" log message if the code somehow gets past the call to `add(1)` _without_ throwing an error:
```jsx {3,8}
try {
@@ -158,9 +158,9 @@ Are you convinced? Let's keep going and see what Redwood brings to the table.
Redwood relies on several packages to do the heavy lifting, but many are wrapped in Redwood's own functionality which makes them even better suited to their individual jobs:
-* [Jest](https://jestjs.io/)
-* [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
-* [Mock Service Worker](https://mswjs.io/) or **msw** for short.
+- [Jest](https://jestjs.io/)
+- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
+- [Mock Service Worker](https://mswjs.io/) or **msw** for short.
Redwood Generators get your test suite bootstrapped. Redwood also includes [Storybook](https://storybook.js.org/), which isn't technically a test suite, but can help in other ways.
@@ -182,7 +182,7 @@ Among other things, Mock Service Worker (msw) lets you simulate the response fro
Storybook itself doesn't appear to be related to testing at all—it's for building and styling components in isolation from your main application—but it can serve as a sanity check for an overlooked part of testing: the user interface. Your tests will only be as good as you write them, and testing things like the alignment of text on the page, the inclusion of images, or animation can be very difficult without investing huge amounts of time and effort. These tests are also very brittle since, depending on how they're written, they can break without any code changes at all! Imagine an integration with a CMS that allows a marketing person to make text/style changes. These changes will probably not be covered by your test suite, but could make your site unusable depending on how bad they are.
-Storybook can provide a quick way to inspect all visual aspects of your site without the tried-and-true method of having a QA person log in and exercise every possible function. Unfortunately, checking those UI elements is not something that Storybook can automate for you, and so can't be part of a continuous integration system. But it makes it *possible* to do so, even if it currently requires a human touch.
+Storybook can provide a quick way to inspect all visual aspects of your site without the tried-and-true method of having a QA person log in and exercise every possible function. Unfortunately, checking those UI elements is not something that Storybook can automate for you, and so can't be part of a continuous integration system. But it makes it _possible_ to do so, even if it currently requires a human touch.
### Redwood Generators
@@ -206,7 +206,6 @@ yarn rw test --no-watch
This one is handy before committing some changes to be sure you didn't inadvertently break something you didn't expect, or before a deploy to production.
-
### Filtering what tests to run
You can run only the web- or api-side test suites by including the side as another argument to the command:
@@ -227,6 +226,7 @@ If you need to be more specific, you can combine side filters, with other filter
```bash
yarn rw test api Comment
```
+
which will only run test specs matching "Comment" in the API side
## Testing Components
@@ -273,6 +273,7 @@ render(, {
)
})
```
+
:::
### Mocking useLocation
@@ -307,11 +308,11 @@ The `allParams` argument accepts an object that will provide parameters as you e
### Queries
-In most cases you will want to exclude the design elements and structure of your components from your test. Then you're free to redesign the component all you want without also having to make the same changes to your test suite. Let's look at some of the functions that React Testing Library provides (they call them "[queries](https://testing-library.com/docs/queries/about/)") that let you check for *parts* of the rendered component, rather than a full string match.
+In most cases you will want to exclude the design elements and structure of your components from your test. Then you're free to redesign the component all you want without also having to make the same changes to your test suite. Let's look at some of the functions that React Testing Library provides (they call them "[queries](https://testing-library.com/docs/queries/about/)") that let you check for _parts_ of the rendered component, rather than a full string match.
#### getByText()
-In our **<Article>** component it seems like we really just want to test that the title of the product is rendered. *How* and *what it looks like* aren't really a concern for this test. Let's update the test to just check for the presence of the title itself:
+In our **<Article>** component it seems like we really just want to test that the title of the product is rendered. _How_ and _what it looks like_ aren't really a concern for this test. Let's update the test to just check for the presence of the title itself:
```jsx {3,7-9} title="web/src/components/Article/Article.test.js"
import { render, screen } from '@redwoodjs/testing/web'
@@ -333,7 +334,7 @@ So, the above test in plain English says "if there is any DOM node containing th
#### queryByText()
-Why not use `getByText()` for everything? Because it will raise an error if the text is *not* found in the document. That means if you want to explicitly test that some text is *not* present, you can't—you'll always get an error.
+Why not use `getByText()` for everything? Because it will raise an error if the text is _not_ found in the document. That means if you want to explicitly test that some text is _not_ present, you can't—you'll always get an error.
Consider an update to our **<Article>** component:
@@ -355,7 +356,7 @@ const Article = ({ article, summary }) => {
export default Article
```
-If we're only displaying the summary of an article then we'll only show the first 100 characters with an ellipsis on the end ("...") and include a link to "Read more" to see the full article. A reasonable test for this component would be that when the `summary` prop is `true` then the "Read more" text should be present. If `summary` is `false` then it should *not* be present. That's where `queryByText()` comes in (relevant test lines are highlighted):
+If we're only displaying the summary of an article then we'll only show the first 100 characters with an ellipsis on the end ("...") and include a link to "Read more" to see the full article. A reasonable test for this component would be that when the `summary` prop is `true` then the "Read more" text should be present. If `summary` is `false` then it should _not_ be present. That's where `queryByText()` comes in (relevant test lines are highlighted):
```jsx {22} title="web/src/components/Article/Article.test.js"
import { render, screen } from '@redwoodjs/testing/web'
@@ -388,7 +389,7 @@ describe('Article', () => {
`getByRole()` allows you to look up elements by their "role", which is an ARIA element that assists in accessibility features. Many HTML elements have a [default role](https://www.w3.org/TR/html-aria/#docconformance) (including `
)}
@@ -529,7 +521,7 @@ What happened here? Notice towards the end of the error message: `Field "postId"
We manually mocked the GraphQL response in the story, and our mock always returns a correct response, regardless of the input!
-There's always a tradeoff when creating mock data—it greatly simplifies testing by not having to rely on the entire GraphQL stack, but that means if you want it to be as accurate as the real thing you basically need to *re-write the real thing in your mock*. In this case, leaving out the `postId` was a one-time fix so it's probably not worth going through the work of creating a story/mock/test that simulates what would happen if we left it off.
+There's always a tradeoff when creating mock data—it greatly simplifies testing by not having to rely on the entire GraphQL stack, but that means if you want it to be as accurate as the real thing you basically need to _re-write the real thing in your mock_. In this case, leaving out the `postId` was a one-time fix so it's probably not worth going through the work of creating a story/mock/test that simulates what would happen if we left it off.
But, if `CommentForm` ended up being a component that was re-used throughout your application, or the code itself will go through a lot of churn because other developers will constantly be making changes to it, it might be worth investing the time to make sure the interface (the props passed to it and the expected return) are exactly what you want them to be.
@@ -1126,7 +1118,7 @@ So it looks like we're just about done here! Try going back to the homepage and
All posts have the same comments! **WHAT HAVE WE DONE??**
-Remember our foreshadowing callout a few pages back, wondering if our `comments()` service which only returns *all* comments could come back to bite us? It finally has: when we get the comments for a post we're not actually getting them for only that post. We're ignoring the `postId` completely and just returning *all* comments in the database! Turns out the old axiom is true: computers only do exactly what you tell them to do.
+Remember our foreshadowing callout a few pages back, wondering if our `comments()` service which only returns _all_ comments could come back to bite us? It finally has: when we get the comments for a post we're not actually getting them for only that post. We're ignoring the `postId` completely and just returning _all_ comments in the database! Turns out the old axiom is true: computers only do exactly what you tell them to do.
Let's fix it!
@@ -1304,7 +1296,7 @@ scenario('returns all comments', async (scenario: StandardScenario) => {
-When the test suite runs everything will still pass. JavaScript won't care if you're passing an argument all of a sudden (although if you were using Typescript you will actually get an error at this point!). In TDD you generally want to get your test to fail before adding code to the thing you're testing which will then cause the test to pass. What's something in this test that will be different once we're only returning *some* comments? How about the number of comments expected to be returned?
+When the test suite runs everything will still pass. JavaScript won't care if you're passing an argument all of a sudden (although if you were using Typescript you will actually get an error at this point!). In TDD you generally want to get your test to fail before adding code to the thing you're testing which will then cause the test to pass. What's something in this test that will be different once we're only returning _some_ comments? How about the number of comments expected to be returned?
Let's take a look at the scenario we're using (remember, it's `standard()` by default):
@@ -1434,7 +1426,7 @@ describe('comments', () => {
-So we're first getting the result from the services, all the comments for a given `postId`. Then we pull the *actual* post from the database and include its comments. Then we expect that the number of comments returned from the service is the same as the number of comments actually attached to the post in the database. Now the test fails and you can see why in the output:
+So we're first getting the result from the services, all the comments for a given `postId`. Then we pull the _actual_ post from the database and include its comments. Then we expect that the number of comments returned from the service is the same as the number of comments actually attached to the post in the database. Now the test fails and you can see why in the output:
```bash
FAIL api api/src/services/comments/comments.test.js
diff --git a/docs/docs/tutorial/chapter6/comments-schema.md b/docs/docs/tutorial/chapter6/comments-schema.md
index 243488167736..3a6dac8139fe 100644
--- a/docs/docs/tutorial/chapter6/comments-schema.md
+++ b/docs/docs/tutorial/chapter6/comments-schema.md
@@ -66,8 +66,8 @@ model Comment {
Most of these lines look very similar to what we've already seen, but this is the first instance of a [relation](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/relations) between two models. `Comment` gets two entries to denote this relationship:
-* `post` which has a type of `Post` and a special `@relation` keyword that tells Prisma how to connect a `Comment` to a `Post`. In this case the field `postId` references the field `id` in `Post`
-* `postId` is just a regular `Int` column which contains the `id` of the `Post` that this comment is referencing
+- `post` which has a type of `Post` and a special `@relation` keyword that tells Prisma how to connect a `Comment` to a `Post`. In this case the field `postId` references the field `id` in `Post`
+- `postId` is just a regular `Int` column which contains the `id` of the `Post` that this comment is referencing
This gives us a classic database model:
@@ -86,13 +86,13 @@ This gives us a classic database model:
Note that there is no real database column named `post` in `Comment`—this is special syntax for Prisma to know how to connect the models together and for you to reference that connection. When you query for a `Comment` using Prisma you can get access to the attached `Post` using that name:
```javascript
-db.comment.findUnique({ where: { id: 1 }}).post()
+db.comment.findUnique({ where: { id: 1 } }).post()
```
Prisma also added a convenience `comments` field to `Post` which gives us the same capability in reverse:
```javascript
-db.post.findUnique({ where: { id: 1 }}).comments()
+db.post.findUnique({ where: { id: 1 } }).comments()
```
### Running the Migration
@@ -107,7 +107,7 @@ When prompted, give this one a name something like "create comment".
:::tip
-You'll need to restart the test suite runner at this point if it's still running. You can do a Ctrl-C or just press `q`. Redwood creates a second, test database for you to run your tests against (it is at `.redwood/test.db` by default). The database migrations are run against that test database whenever the test suite is *started*, not while it's running, so you'll need to restart it to test against the new database structure.
+You'll need to restart the test suite runner at this point if it's still running. You can do a Ctrl-C or just press `q`. Redwood creates a second, test database for you to run your tests against (it is at `.redwood/test.db` by default). The database migrations are run against that test database whenever the test suite is _started_, not while it's running, so you'll need to restart it to test against the new database structure.
:::
@@ -330,7 +330,7 @@ query CommentsQuery {
:::info
-Have you noticed that something may be amiss? The `comments()` function returns *all* comments, and all comments only. Could this come back to bite us?
+Have you noticed that something may be amiss? The `comments()` function returns _all_ comments, and all comments only. Could this come back to bite us?
Hmmm...
@@ -557,7 +557,7 @@ What is this `scenario()` function? That's made available by Redwood that mostly
Yes, all things being equal it would be great to not have these tests depend on a piece of software outside of our control.
-However, the difference here is that in a service almost all of the logic you write will depend on moving data in and out of a database and it's much simpler to just let that code run and *really* access the database, rather than trying to mock and intercept each and every possible call that Prisma could make.
+However, the difference here is that in a service almost all of the logic you write will depend on moving data in and out of a database and it's much simpler to just let that code run and _really_ access the database, rather than trying to mock and intercept each and every possible call that Prisma could make.
Not to mention that Prisma itself is currently under development and implementations could change at any time. Trying to keep pace with those changes and constantly keep mocks in sync would be a nightmare!
@@ -630,11 +630,11 @@ The exported scenario here is named "standard." Remember when we worked on compo
The nested structure of a scenario is defined like this:
-* **comment**: the name of the model this data is for
- * **one, two**: a friendly name given to the scenario data which you can reference in your tests
- * **data**: contains the actual data that will be put in the database
- * **name, body, post**: fields that correspond to the schema. In this case a **Comment** requires that it be related to a **Post**, so the scenario has a `post` key and values as well (using Prisma's [nested create syntax](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes))
- * **select, include**: optionally, to customize the object to `select` or `include` related fields [using Prisma's syntax](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#create-a-related-record)
+- **comment**: the name of the model this data is for
+ - **one, two**: a friendly name given to the scenario data which you can reference in your tests
+ - **data**: contains the actual data that will be put in the database
+ - **name, body, post**: fields that correspond to the schema. In this case a **Comment** requires that it be related to a **Post**, so the scenario has a `post` key and values as well (using Prisma's [nested create syntax](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes))
+ - **select, include**: optionally, to customize the object to `select` or `include` related fields [using Prisma's syntax](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#create-a-related-record)
When you receive the `scenario` argument in your test, the `data` key gets unwrapped so that you can reference fields like `scenario.comment.one.name`.
@@ -660,10 +660,10 @@ export const standard = defineScenario({
post: {
create: {
title: 'Redwood Leaves',
- body: 'The quick brown fox jumped over the lazy dog.'
- }
- }
- }
+ body: 'The quick brown fox jumped over the lazy dog.',
+ },
+ },
+ },
},
john: {
data: {
@@ -672,13 +672,13 @@ export const standard = defineScenario({
post: {
create: {
title: 'Root Systems',
- body: 'The five boxing wizards jump quickly.'
- }
- }
- }
- }
+ body: 'The five boxing wizards jump quickly.',
+ },
+ },
+ },
+ },
// highlight-end
- }
+ },
})
```
@@ -698,10 +698,10 @@ export const standard = defineScenario({
post: {
create: {
title: 'Redwood Leaves',
- body: 'The quick brown fox jumped over the lazy dog.'
- }
- }
- }
+ body: 'The quick brown fox jumped over the lazy dog.',
+ },
+ },
+ },
},
john: {
data: {
@@ -711,12 +711,12 @@ export const standard = defineScenario({
create: {
title: 'Root Systems',
body: 'The five boxing wizards jump quickly.',
- }
- }
- }
- }
+ },
+ },
+ },
+ },
// highlight-end
- }
+ },
})
```
@@ -746,9 +746,9 @@ export const postOnly = defineScenario({
data: {
title: 'Bark',
body: "A tree's bark is worse than its bite",
- }
- }
- }
+ },
+ },
+ },
})
// highlight-end
```
@@ -770,9 +770,9 @@ export const postOnly = defineScenario({
data: {
title: 'Bark',
body: "A tree's bark is worse than its bite",
- }
- }
- }
+ },
+ },
+ },
})
// highlight-end
@@ -893,12 +893,13 @@ interface CreateCommentArgs {
input: Prisma.CommentCreateInput | Prisma.CommentUncheckedCreateInput
}
```
+
in case we wanted to allow both ways – which Prisma generally allows, however [it doesn't allow to pick and mix](https://stackoverflow.com/a/69169106/1246547) within the same input.
:::
-We'll test that all the fields we give to the `createComment()` function are actually created in the database, and for good measure just make sure that `createdAt` is set to a non-null value. We could test that the actual timestamp is correct, but that involves freezing the JavaScript Date object so that no matter how long the test takes, you can still compare the value to `new Date` which is right *now*, down to the millisecond. While possible, it's beyond the scope of our easy, breezy tutorial since it gets [very gnarly](https://codewithhugo.com/mocking-the-current-date-in-jest-tests/)!
+We'll test that all the fields we give to the `createComment()` function are actually created in the database, and for good measure just make sure that `createdAt` is set to a non-null value. We could test that the actual timestamp is correct, but that involves freezing the JavaScript Date object so that no matter how long the test takes, you can still compare the value to `new Date` which is right _now_, down to the millisecond. While possible, it's beyond the scope of our easy, breezy tutorial since it gets [very gnarly](https://codewithhugo.com/mocking-the-current-date-in-jest-tests/)!
:::info What's up with the names for scenario data? `posts.bark`? Really?
@@ -924,9 +925,9 @@ Maybe a [mnemonic](https://www.mnemonicgenerator.com/?words=M%20W%20S%20A) would
**M**ocks : **W**eb :: **S**cenarios : **A**PI:
-* Mysterious Weasels Scratched Armor
-* Minesweepers Wrecked Subliminal Attorneys
-* Martian Warriors Squeezed Apricots
+- Mysterious Weasels Scratched Armor
+- Minesweepers Wrecked Subliminal Attorneys
+- Martian Warriors Squeezed Apricots
Maybe not...
diff --git a/docs/docs/tutorial/chapter6/multiple-comments.md b/docs/docs/tutorial/chapter6/multiple-comments.md
index 0ac48bf9578f..84f7b87e7376 100644
--- a/docs/docs/tutorial/chapter6/multiple-comments.md
+++ b/docs/docs/tutorial/chapter6/multiple-comments.md
@@ -2,11 +2,11 @@
Our amazing blog posts will obviously garner a huge and passionate fanbase and we will very rarely have only a single comment. Let's work on displaying a list of comments.
-Let's think about where our comments are being displayed. Probably not on the homepage, since that only shows a summary of each post. A user would need to go to the full page to show the comments for that blog post. But that page is only fetching the data for the single blog post itself, nothing else. We'll need to get the comments and since we'll be fetching *and* displaying them, that sounds like a job for a Cell.
+Let's think about where our comments are being displayed. Probably not on the homepage, since that only shows a summary of each post. A user would need to go to the full page to show the comments for that blog post. But that page is only fetching the data for the single blog post itself, nothing else. We'll need to get the comments and since we'll be fetching _and_ displaying them, that sounds like a job for a Cell.
:::info Couldn't the query for the blog post page also fetch the comments?
-Yes, it could! But the idea behind Cells is to make components even more [composable](https://en.wikipedia.org/wiki/Composability) by having them be responsible for their own data fetching *and* display. If we rely on a blog post to fetch the comments then the new Comments component we're about to create now requires something *else* to fetch the comments and pass them in. If we re-use the Comments component somewhere, now we're fetching comments in two different places.
+Yes, it could! But the idea behind Cells is to make components even more [composable](https://en.wikipedia.org/wiki/Composability) by having them be responsible for their own data fetching _and_ display. If we rely on a blog post to fetch the comments then the new Comments component we're about to create now requires something _else_ to fetch the comments and pass them in. If we re-use the Comments component somewhere, now we're fetching comments in two different places.
**But what about the Comment component we just made, why doesn't that fetch its own data?**
@@ -14,7 +14,7 @@ There aren't any instances I (the author) could think of where we would ever wan
**Then why make a standalone Comment component at all? Why not just do all the display in the CommentsCell?**
-We're trying to start in small chunks to make the tutorial more digestible for a new audience so we're starting simple and getting more complex as we go. But it also just feels *nice* to build up a UI from these smaller chunks that are easier to reason about and keep separate in your head.
+We're trying to start in small chunks to make the tutorial more digestible for a new audience so we're starting simple and getting more complex as we go. But it also just feels _nice_ to build up a UI from these smaller chunks that are easier to reason about and keep separate in your head.
**But what about—**
@@ -112,13 +112,13 @@ export const Failure = ({ error }: CellFailureProps) => (
export const Success = ({ comments }: CellSuccessProps) => {
return (
- // highlight-start
+ // highlight-start
<>
{comments.map((comment) => (
))}
>
- // highlight-end
+ // highlight-end
)
}
```
@@ -221,7 +221,7 @@ export const Success = ({ comments }) => {
{comments.map((comment) => (
))}
- // highlight-next-line
+ // highlight-next-line
)
}
@@ -232,7 +232,7 @@ export const Success = ({ comments }) => {
:::tip
-`space-y-8` is a handy Tailwind class that puts a space *between* elements, but not above or below the entire stack (which is what would happen if you gave each `` its own top/bottom margin).
+`space-y-8` is a handy Tailwind class that puts a space _between_ elements, but not above or below the entire stack (which is what would happen if you gave each `` its own top/bottom margin).
:::
@@ -312,7 +312,7 @@ export default Article
-If we are *not* showing the summary, then we'll show the comments. Take a look at the **Full** and **Summary** stories in Storybook and you should see comments on one and not on the other.
+If we are _not_ showing the summary, then we'll show the comments. Take a look at the **Full** and **Summary** stories in Storybook and you should see comments on one and not on the other.
:::info Shouldn't the `CommentsCell` cause an actual GraphQL request? How does this work?
@@ -399,10 +399,10 @@ We added a component, `CommentsCell`, and edited another, `Article`, so what do
The actual `Comment` component does most of the work so there's no need to test all of that functionality again in `CommentsCell`: our `Comment` tests cover that just fine. What things does `CommentsCell` do that make it unique?
-* Has a loading message
-* Has an error message
-* Has a failure message
-* When it renders successfully, it outputs as many comments as were returned by the `QUERY` (*what* is rendered we'll leave to the `Comment` tests)
+- Has a loading message
+- Has an error message
+- Has a failure message
+- When it renders successfully, it outputs as many comments as were returned by the `QUERY` (_what_ is rendered we'll leave to the `Comment` tests)
The default `CommentsCell.test.{jsx,tsx}` actually tests every state for us, albeit at an absolute minimum level—it makes sure no errors are thrown:
@@ -568,17 +568,16 @@ describe('CommentsCell', () => {
// highlight-end
})
})
-
```
-We're looping through each `comment` from the mock, the same mock used by Storybook, so that even if we add more later, we're covered. You may find yourself writing a test and saying "just test that there are two total comments," which will work today, but months from now when you add more comments to the mock to try some different iterations in Storybook, that test will start failing. Avoid hardcoding data like this, especially [magic numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)), into your test when you can derive it from your mocked data!
+We're looping through each `comment` from the mock, the same mock used by Storybook, so that even if we add more later, we're covered. You may find yourself writing a test and saying "just test that there are two total comments," which will work today, but months from now when you add more comments to the mock to try some different iterations in Storybook, that test will start failing. Avoid hardcoding data like this, especially [magic numbers](), into your test when you can derive it from your mocked data!
#### Testing Article
-The functionality we added to `Article` says to show the comments for the post if we are *not* showing the summary. We've got a test for both the "full" and "summary" renders already. Generally you want a test to be testing "one thing," like whether the body of the article is present, and another test for whether the comments are displaying. If you find that you're using "and" in your test description (like "renders a blog post and its comments") that's a good sign that it should probably be split into two separate tests.
+The functionality we added to `Article` says to show the comments for the post if we are _not_ showing the summary. We've got a test for both the "full" and "summary" renders already. Generally you want a test to be testing "one thing," like whether the body of the article is present, and another test for whether the comments are displaying. If you find that you're using "and" in your test description (like "renders a blog post and its comments") that's a good sign that it should probably be split into two separate tests.
Let's add two additional tests for our new functionality:
@@ -715,7 +714,7 @@ We're introducing a new test function here, `waitFor()`, which will wait for thi
:::info
-The summary version of `Article` does *not* render the `CommentsCell`, but we should still wait. Why? If we did mistakenly start including `CommentsCell`, but didn't wait for the render, we would get a falsely passing test—indeed the text isn't on the page but that's because it's still showing the `Loading` component! If we had waited we would have seen the actual comment body get rendered, and the test would (correctly) fail.
+The summary version of `Article` does _not_ render the `CommentsCell`, but we should still wait. Why? If we did mistakenly start including `CommentsCell`, but didn't wait for the render, we would get a falsely passing test—indeed the text isn't on the page but that's because it's still showing the `Loading` component! If we had waited we would have seen the actual comment body get rendered, and the test would (correctly) fail.
:::
diff --git a/docs/docs/tutorial/chapter6/the-redwood-way.md b/docs/docs/tutorial/chapter6/the-redwood-way.md
index 316154c266d1..d0ec7c88f7f9 100644
--- a/docs/docs/tutorial/chapter6/the-redwood-way.md
+++ b/docs/docs/tutorial/chapter6/the-redwood-way.md
@@ -9,7 +9,7 @@ There are two main features we need to build:
1. Comment form and creation
2. Comment retrieval and display
-Which order we build them in is up to us. To ease into things, let's start with the fetching and displaying comments first and then we'll move on to more complex work of adding a form and service to create a new comment. Of course, this is Redwood, so even forms and services aren't *that* complex!
+Which order we build them in is up to us. To ease into things, let's start with the fetching and displaying comments first and then we'll move on to more complex work of adding a form and service to create a new comment. Of course, this is Redwood, so even forms and services aren't _that_ complex!
### Storybook
@@ -124,7 +124,7 @@ export const generated = () => {
comment={{
name: 'Rob Cameron',
body: 'This is the first comment!',
- createdAt: '2020-01-01T12:34:56Z'
+ createdAt: '2020-01-01T12:34:56Z',
}}
/>
)
diff --git a/docs/docs/tutorial/chapter7/api-side-currentuser.md b/docs/docs/tutorial/chapter7/api-side-currentuser.md
index bce5ffaa2efd..f781421ec8ff 100644
--- a/docs/docs/tutorial/chapter7/api-side-currentuser.md
+++ b/docs/docs/tutorial/chapter7/api-side-currentuser.md
@@ -144,7 +144,7 @@ Now run `yarn rw prisma migrate reset` and and...you'll get a different error. B
:::
-We've got an error here because running a database `reset` doesn't also apply pending migrations. So we're trying to set a `userId` where one doesn't exist in the database (it does exist in Prisma generated client libs though, so it thinks that there *should* be one, even if it doesn't exist in the database yet).
+We've got an error here because running a database `reset` doesn't also apply pending migrations. So we're trying to set a `userId` where one doesn't exist in the database (it does exist in Prisma generated client libs though, so it thinks that there _should_ be one, even if it doesn't exist in the database yet).
It may feel like we're stuck, but note that the database did reset successfully, it's just the seed that failed. So now let's migrate the database to add the new `userId` to `Post`, and then re-run the seed to populate the database, naming it something like "add userId to post":
@@ -200,7 +200,7 @@ To enable this we'll need to make two modifications on the api side:
:::info What about the mutations?
-We did *not* add `user` or `userId` to the `CreatePostInput` or `UpdatePostInput` types. Although we want to set a user on each newly created post, we don't want just anyone to do that via a GraphQL call! You could easily create or edit a post and assign it to someone else by just modifying the GraphQL payload. We'll save assigning the user to just the service, so it can't be manipulated by the outside world.
+We did _not_ add `user` or `userId` to the `CreatePostInput` or `UpdatePostInput` types. Although we want to set a user on each newly created post, we don't want just anyone to do that via a GraphQL call! You could easily create or edit a post and assign it to someone else by just modifying the GraphQL payload. We'll save assigning the user to just the service, so it can't be manipulated by the outside world.
:::
@@ -270,8 +270,7 @@ We could also write this resolver as follows:
```javascript
export const Post = {
- user: (_obj, { root }) =>
- db.user.findFirst({ where: { id: root.userId } }),
+ user: (_obj, { root }) => db.user.findFirst({ where: { id: root.userId } }),
}
```
@@ -279,7 +278,7 @@ Note that if you keep the relation resolver above, but also included a `user` pr
:::info Prisma and the N+1 Problem
-If you have any experience with database design and retrieval you may have noticed this method presents a less than ideal solution: for every post that's found, you need to perform an *additional* query just to get the user data associated with that `post`, also known as the [N+1 problem](https://medium.com/the-marcy-lab-school/what-is-the-n-1-problem-in-graphql-dd4921cb3c1a). This is just due to the nature of GraphQL queries: each resolver function really only knows about its own parent object, nothing about potential children.
+If you have any experience with database design and retrieval you may have noticed this method presents a less than ideal solution: for every post that's found, you need to perform an _additional_ query just to get the user data associated with that `post`, also known as the [N+1 problem](https://medium.com/the-marcy-lab-school/what-is-the-n-1-problem-in-graphql-dd4921cb3c1a). This is just due to the nature of GraphQL queries: each resolver function really only knows about its own parent object, nothing about potential children.
There have been several attempts to work around this issue. A simple one that includes no extra dependencies is to remove this field resolver and simply include `user` data along with any `post` you retrieve from the database:
@@ -288,8 +287,8 @@ export const post = ({ id }) => {
return db.post.findUnique({
where: { id },
include: {
- user: true
- }
+ user: true,
+ },
})
}
```
@@ -395,13 +394,13 @@ Depending on whether you started from the Redwood Tutorial repo or not, you may
## Accessing `currentUser` on the API side
-There's a magical variable named `context` that's available within any of your service functions. It contains the context in which the service function is being called. One property available on this context is the user that's logged in (*if* someone is logged in). It's the same `currentUser` that is available on the web side:
+There's a magical variable named `context` that's available within any of your service functions. It contains the context in which the service function is being called. One property available on this context is the user that's logged in (_if_ someone is logged in). It's the same `currentUser` that is available on the web side:
```javascript title="api/src/service/posts/posts.js"
export const createPost = ({ input }) => {
return db.post.create({
// highlight-next-line
- data: { ...input, userId: context.currentUser.id }
+ data: { ...input, userId: context.currentUser.id },
})
}
```
@@ -465,7 +464,7 @@ export const Post = {
:::info Prisma's `findUnique()` vs. `findFirst()`
-Note that we switched from `findUnique()` to `findFirst()` here. Prisma's `findUnique()` requires that any attributes in the `where` clause have unique indexes, which `id` does, but `userId` does not. So we need to switch to the `findFirst()` function which allows you to put whatever you want in the `where`, which may return more than one record, but Prisma will only return the first of that set. In this case we know there'll always only be one, because we're selecting by `id` *in addition* to `userId`.
+Note that we switched from `findUnique()` to `findFirst()` here. Prisma's `findUnique()` requires that any attributes in the `where` clause have unique indexes, which `id` does, but `userId` does not. So we need to switch to the `findFirst()` function which allows you to put whatever you want in the `where`, which may return more than one record, but Prisma will only return the first of that set. In this case we know there'll always only be one, because we're selecting by `id` _in addition_ to `userId`.
:::
@@ -473,7 +472,7 @@ These changes make sure that a user can only see a list of their own posts, or t
What about `updatePost` and `deletePost`? They aren't limited to just the `currentUser`, which would let anyone update or delete a post if they made a manual GraphQL call! That's not good. We'll deal with those [a little later](#update-and-delete).
-But there's a problem with the updates we just made: doesn't the homepage also use the `posts` service to display all the articles for the homepage? This code update would limit the homepage to only showing a logged in user's own posts and no one else! And what happens if someone who is *not* logged in goes to the homepage? ERROR.
+But there's a problem with the updates we just made: doesn't the homepage also use the `posts` service to display all the articles for the homepage? This code update would limit the homepage to only showing a logged in user's own posts and no one else! And what happens if someone who is _not_ logged in goes to the homepage? ERROR.
How can we return one list of posts in the admin, and a different list of posts for the homepage?
@@ -481,7 +480,7 @@ How can we return one list of posts in the admin, and a different list of posts
We could go down the road of adding variables in the GraphQL queries, along with checks in the existing `posts` service, that return a different list of posts whether you're on the homepage or in the admin. But this complexity adds a lot of surface area to test and some fragility if someone goes in there in the future—they have to be very careful not to add a new condition or negate an existing one and accidentally expose your admin functionality to exploits.
-What if we created *new* GraphQL queries for the admin views of posts? They would have automatic security checks thanks to `@requireAdmin`, no custom code required. These new queries will be used in the admin posts pages, and the original, simple `posts` service will be used for the homepage and article detail page.
+What if we created _new_ GraphQL queries for the admin views of posts? They would have automatic security checks thanks to `@requireAdmin`, no custom code required. These new queries will be used in the admin posts pages, and the original, simple `posts` service will be used for the homepage and article detail page.
There are several steps we'll need to complete:
@@ -512,7 +511,8 @@ export const schema = gql`
type Mutation {
createPost(input: CreatePostInput!): Post! @requireAuth(roles: ["admin"])
- updatePost(id: Int!, input: UpdatePostInput!): Post! @requireAuth(roles: ["admin"])
+ updatePost(id: Int!, input: UpdatePostInput!): Post!
+ @requireAuth(roles: ["admin"])
deletePost(id: Int!): Post! @requireAuth(roles: ["admin"])
}
`
@@ -757,25 +757,24 @@ export const deletePost = async ({ id }) => {
where: { id },
})
}
-
```
## Wrapping Up
Whew! Let's try several different scenarios (this is the kind of thing that the QA team lives for), making sure everything is working as expected:
-* A logged out user *should* see all posts on the homepage
-* A logged out user *should* be able to see the detail for a single post
-* A logged out user *should not* be able to go to /admin/posts
-* A logged out user *should not* see moderation controls next to comments
-* A logged in admin user *should* see all articles on the homepage (not just their own)
-* A logged in admin user *should* be able to go to /admin/posts
-* A logged in admin user *should* be able to create a new post
-* A logged in admin user *should not* be able to see anyone else's posts in /admin/posts
-* A logged in admin user *should not* see moderation controls next to comments (unless you modified that behavior at the end of the last page)
-* A logged in moderator user *should* see moderation controls next to comments
-* A logged in moderator user *should not* be able to access /admin/posts
-
-In fact, you could write some new tests to make sure this functionality doesn't mistakenly change in the future. The quickest would probably be to create `adminPosts.scenarios.js` and `adminPosts.test.js` files to go with the new service and verify that you are only returned the posts owned by a given user. You can [mock currentUser](/docs/testing#mockcurrentuser-on-the-api-side) to simulate someone being logged in or not, with different roles. You could add tests for the Cells we modified above, but the data they get is dependent on what's returned from the service, so as long as you have the service itself covered you should be okay. The 100% coverage folks would argue otherwise, but while they're still busy writing tests we're out cruising in our new yacht thanks to all the revenue from our newly launched (with *reasonable* test coverage) features!
+- A logged out user _should_ see all posts on the homepage
+- A logged out user _should_ be able to see the detail for a single post
+- A logged out user _should not_ be able to go to /admin/posts
+- A logged out user _should not_ see moderation controls next to comments
+- A logged in admin user _should_ see all articles on the homepage (not just their own)
+- A logged in admin user _should_ be able to go to /admin/posts
+- A logged in admin user _should_ be able to create a new post
+- A logged in admin user _should not_ be able to see anyone else's posts in /admin/posts
+- A logged in admin user _should not_ see moderation controls next to comments (unless you modified that behavior at the end of the last page)
+- A logged in moderator user _should_ see moderation controls next to comments
+- A logged in moderator user _should not_ be able to access /admin/posts
+
+In fact, you could write some new tests to make sure this functionality doesn't mistakenly change in the future. The quickest would probably be to create `adminPosts.scenarios.js` and `adminPosts.test.js` files to go with the new service and verify that you are only returned the posts owned by a given user. You can [mock currentUser](/docs/testing#mockcurrentuser-on-the-api-side) to simulate someone being logged in or not, with different roles. You could add tests for the Cells we modified above, but the data they get is dependent on what's returned from the service, so as long as you have the service itself covered you should be okay. The 100% coverage folks would argue otherwise, but while they're still busy writing tests we're out cruising in our new yacht thanks to all the revenue from our newly launched (with _reasonable_ test coverage) features!
Did it work? Great! Did something go wrong? Can someone see too much, or too little? Double check that all of your GraphQL queries are updated and you've saved changes in all the opened files.
diff --git a/docs/docs/tutorial/chapter7/rbac.md b/docs/docs/tutorial/chapter7/rbac.md
index 26c2d36809c6..43abde2472af 100644
--- a/docs/docs/tutorial/chapter7/rbac.md
+++ b/docs/docs/tutorial/chapter7/rbac.md
@@ -1,6 +1,6 @@
# Role-Based Access Control (RBAC)
-Imagine a few weeks in the future of our blog when every post hits the front page of the New York Times and we're getting hundreds of comments a day. We can't be expected to come up with quality content each day *and* moderate the endless stream of (mostly well-meaning) comments! We're going to need help. Let's hire a comment moderator to remove obvious spam and any comments that don't heap praise on our writing ability. You know, to help make the internet a better place.
+Imagine a few weeks in the future of our blog when every post hits the front page of the New York Times and we're getting hundreds of comments a day. We can't be expected to come up with quality content each day _and_ moderate the endless stream of (mostly well-meaning) comments! We're going to need help. Let's hire a comment moderator to remove obvious spam and any comments that don't heap praise on our writing ability. You know, to help make the internet a better place.
We already have a login system for our blog, but right now it's all-or-nothing: you either get access to create blog posts, or you don't. In this case our comment moderator(s) will need logins so that we know who they are, but we're not going to let them create new blog posts. We need some kind of role that we can give to our two kinds of users so we can distinguish them from one another.
@@ -26,7 +26,6 @@ model User {
Next we'll (try) to migrate the database:
-
```bash
yarn rw prisma migrate dev
```
@@ -117,7 +116,7 @@ export const hasRole = (roles: AllowedRoles): boolean => {
return currentUserRoles?.some((allowedRole) => roles === allowedRole)
}
}
- ```
+```
This is because we now know that the type of `currentUser.roles` is a `string` based on the type being returned from Prisma. So you can safely remove the block of code where it's checking if roles is an array:
@@ -183,9 +182,19 @@ The easiest way to prevent access to an entire URL is via the Router. The `
-
+
-
+
@@ -246,12 +255,12 @@ Let's create a new user that will represent the comment moderator. Since this is
:::tip The Plus Trick
-The Plus Trick is a very handy feature of the email standard known as a "boxname", the idea being that you may have other incoming boxes besides one just named "Inbox" and by adding `+something` to your email address you can specify which box the mail should be sorted into. They don't appear to be in common use these days, but they are ridiculously helpful for us developers when we're constantly needing new email addresses for testing: it gives us an infinite number of *valid* email addresses—they all come to your regular inbox!
+The Plus Trick is a very handy feature of the email standard known as a "boxname", the idea being that you may have other incoming boxes besides one just named "Inbox" and by adding `+something` to your email address you can specify which box the mail should be sorted into. They don't appear to be in common use these days, but they are ridiculously helpful for us developers when we're constantly needing new email addresses for testing: it gives us an infinite number of _valid_ email addresses—they all come to your regular inbox!
Just append +something to your email address before the @:
-* `jane.doe+testing@example.com` will go to `jane.doe@example.com`
-* `dom+20210909@example.com` will go to `dom@example.com`
+- `jane.doe+testing@example.com` will go to `jane.doe@example.com`
+- `dom+20210909@example.com` will go to `dom@example.com`
Note that not all providers support this plus-based syntax, but the major ones (Gmail, Yahoo, Microsoft, Apple) do. If you find that you're not receiving emails at your own domain, you may want to create a free account at one of these providers just to use for testing.
@@ -266,13 +275,17 @@ If you disabled the new user signup as suggested at the end of the first part of
```javascript
const CryptoJS = require('crypto-js')
const salt = CryptoJS.lib.WordArray.random(128 / 8).toString()
-const hashedPassword = CryptoJS.PBKDF2('password', salt, { keySize: 256 / 32 }).toString()
-db.user.create({ data: { email: 'moderator@moderator.com', hashedPassword, salt } })
+const hashedPassword = CryptoJS.PBKDF2('password', salt, {
+ keySize: 256 / 32,
+}).toString()
+db.user.create({
+ data: { email: 'moderator@moderator.com', hashedPassword, salt },
+})
```
:::
-Now if you log out as the admin and log in as the moderator you should *not* have access to the posts admin.
+Now if you log out as the admin and log in as the moderator you should _not_ have access to the posts admin.
### Restrict Access in a Component
diff --git a/docs/docs/tutorial/intermission.md b/docs/docs/tutorial/intermission.md
index 793730157af2..9fe7cdc2b74d 100644
--- a/docs/docs/tutorial/intermission.md
+++ b/docs/docs/tutorial/intermission.md
@@ -22,7 +22,7 @@ yarn rw setup ui tailwindcss
However, none of the screenshots that follow will come anywhere close to what you're seeing in your browser (except for those isolated components you build in Storybook) so you may want to just start with the [example repo](https://github.com/redwoodjs/redwood-tutorial). You'll also be missing out on a good starting test suite that we've added!
-If you're *still* set on continuing with your own repo, and you deployed to a service like Netlify, you would have changed the database provider in `schema.prisma` to `postgresql`. If that's the case then make sure your local development environment has changed over as well. Check out the [Local Postgres Setup](../local-postgres-setup.md) for assistance. If you stick with the [example repo](https://github.com/redwoodjs/redwood-tutorial) instead, you can go ahead with good ol' SQLite (what we were using locally to build everything in the first half).
+If you're _still_ set on continuing with your own repo, and you deployed to a service like Netlify, you would have changed the database provider in `schema.prisma` to `postgresql`. If that's the case then make sure your local development environment has changed over as well. Check out the [Local Postgres Setup](../local-postgres-setup.md) for assistance. If you stick with the [example repo](https://github.com/redwoodjs/redwood-tutorial) instead, you can go ahead with good ol' SQLite (what we were using locally to build everything in the first half).
Once you're ready, start up the dev server:
diff --git a/docs/docs/typescript/generated-types.md b/docs/docs/typescript/generated-types.md
index 644cc7b39bd4..f49463c5f2bb 100644
--- a/docs/docs/typescript/generated-types.md
+++ b/docs/docs/typescript/generated-types.md
@@ -38,13 +38,13 @@ For example, if you specify the return type on `getCurrentUser` as...
```ts title="api/src/lib/auth.ts"
interface MyCurrentUser {
- id: string,
- roles: string[],
- email: string,
+ id: string
+ roles: string[]
+ email: string
projectId: number
}
-const getCurrentUser = ({decoded}): MyCurrentUser => {
+const getCurrentUser = ({ decoded }): MyCurrentUser => {
//..
}
```
@@ -77,7 +77,10 @@ Redwood generates types for both the data returned from the query and the query'
These generated types will use the query's name—in this case, `FindBlogPostQuery`—so you can import them like this:
```ts title="web/src/components/BlogPostCell.tsx"
-import type { FindBlogPostQuery, FindBlogPostQueryVariables } from 'types/graphql'
+import type {
+ FindBlogPostQuery,
+ FindBlogPostQueryVariables,
+} from 'types/graphql'
```
`FindBlogPostQuery` is the type of the data returned from the query (`{ title: string, body: string }`) and `FindBlogPostQueryVariables` is the type of the query's variables (`{ id: number }`).
diff --git a/docs/docs/typescript/introduction.md b/docs/docs/typescript/introduction.md
index eb881419fac1..7cfe1732f8ae 100644
--- a/docs/docs/typescript/introduction.md
+++ b/docs/docs/typescript/introduction.md
@@ -115,7 +115,7 @@ Add you custom `@adminUI` alias to your `tsconfig.json` file:
You might have noticed the `"../.redwood/types/mirror/web/src/components/modules/admin/common/ui/*"` path. I'm glad you did!
-When you build your project redwood will create a set of directories or a virtual directory called`.redwood`, [read more about this typescript feature here](https://www.typescriptlang.org/docs/handbook/module-resolution.html#virtual-directories-with-rootdirs). This directory contains types for te Cells, so there is no need for us to specify an index file.
+When you build your project redwood will create a set of directories or a virtual directory called`.redwood`, [read more about this typescript feature here](https://www.typescriptlang.org/docs/handbook/module-resolution.html#virtual-directories-with-rootdirs). This directory contains types for te Cells, so there is no need for us to specify an index file.
When you combine those two paths `.src/...` and `./.redwood/...` under an alias you can have shorter and cleaner import paths:
@@ -130,5 +130,5 @@ import { CustomModal } from '@adminUI/CustomModal'
#### Some benefits of using alias paths are
1. **Improved code readability**, by abstracting complex directory hierarchies, and having meaningful names for your imports.
-1. **Code maintainability**, aliases allow you to decouple your code from the file structure and more easily move files around, as they are not tied to the longer path.
+1. **Code maintainability**, aliases allow you to decouple your code from the file structure and more easily move files around, as they are not tied to the longer path.
1. **Reduce boilerplate**, no more `../../src/components/modules/admin/common/ui/` 😮💨
diff --git a/docs/docs/typescript/strict-mode.md b/docs/docs/typescript/strict-mode.md
index 221d7e6fe98b..e83cd4ab3a11 100644
--- a/docs/docs/typescript/strict-mode.md
+++ b/docs/docs/typescript/strict-mode.md
@@ -18,7 +18,7 @@ Enable strict mode by setting `strict` to true in `web/tsconfig.json` and `api/t
"noEmit": true,
"allowJs": true,
// highlight-next-line
- "strict": true,
+ "strict": true
// ...
}
// ...
@@ -49,9 +49,9 @@ For most cases however, you probably want to convert nulls to undefined - one wa
```ts title="api/src/services/users.ts"
// highlight-next-line
-import { removeNulls } from "@redwoodjs/api"
+import { removeNulls } from '@redwoodjs/api'
-export const updateUser: MutationResolvers["updateUser"] = ({ id, input }) => {
+export const updateUser: MutationResolvers['updateUser'] = ({ id, input }) => {
return db.user.update({
// highlight-next-line
data: removeNulls(input),
@@ -108,7 +108,6 @@ export const Post: PostRelationResolvers = {
}
```
-
:::tip An optimization tip
If the relation truly is required, it may make more sense to include `author` in your `post` Service's Prisma query and modify the `Post.author` resolver accordingly:
@@ -167,7 +166,7 @@ If your `getCurrentUser` doesn't return `roles`, and you don't use this function
#### B. Roles on current user is a string
-Alternatively, if you define the roles as a string, you can remove the code that does checks against Arrays
+Alternatively, if you define the roles as a string, you can remove the code that does checks against Arrays
```diff title="api/src/lib/auth.ts"
export const hasRole = (roles: AllowedRoles): boolean => {
@@ -236,6 +235,7 @@ export const hasRole = (roles: AllowedRoles): boolean => {
return false
}
```
+
### `getCurrentUser` in `api/src/lib/auth.ts`
@@ -250,15 +250,15 @@ import type { AuthContextPayload } from '@redwoodjs/api'
// Example 1: typing directly
export const getCurrentUser: CurrentUserFunc = async (
- decoded: { id: string, name: string },
- { token, type }: { token: string, type: string },
+ decoded: { id: string; name: string },
+ { token, type }: { token: string; type: string }
) => {
// ...
}
// Example 2: Using AuthContextPayload
export const getCurrentUser: CurrentUserFunc = async (
- decoded: { id: string, name: string },
+ decoded: { id: string; name: string },
{ token, type }: AuthContextPayload[1],
{ event, context }: AuthContextPayload[2]
) => {
diff --git a/docs/docs/typescript/utility-types.md b/docs/docs/typescript/utility-types.md
index a01895f1adfd..a12ccab4a86f 100644
--- a/docs/docs/typescript/utility-types.md
+++ b/docs/docs/typescript/utility-types.md
@@ -17,14 +17,17 @@ This is used to type the props of your Cell's `Success` component.
It takes two arguments as generics:
| Generic | Description |
-|:-------------|:-----------------------------------------------------------------------------------------|
+| :----------- | :--------------------------------------------------------------------------------------- |
| `TData` | The type of data you're expecting to receive (usually the type generated from the query) |
| `TVariables` | An optional second parameter for the type of the query's variables |
Not only does `CellSuccessProps` type the data returned from the query, but it also types the variables and methods returned by Apollo Client's `useQuery` hook!
```ts title="web/src/components/BlogPostCell.tsx"
-import type { FindBlogPostQuery, FindBlogPostQueryVariables } from 'types/graphql'
+import type {
+ FindBlogPostQuery,
+ FindBlogPostQueryVariables,
+} from 'types/graphql'
// highlight-next-line
import type { CellSuccessProps } from '@redwoodjs/web'
@@ -32,12 +35,15 @@ import type { CellSuccessProps } from '@redwoodjs/web'
// ...
// highlight-next-line
-type SuccessProps = CellSuccessProps
+type SuccessProps = CellSuccessProps<
+ FindBlogPostQuery,
+ FindBlogPostQueryVariables
+>
export const Success = ({
blogPost, // From the query. This is typed of course
- queryResult // 👈 From Apollo Client. This is typed too!
-// highlight-next-line
+ queryResult, // 👈 From Apollo Client. This is typed too!
+ // highlight-next-line
}: SuccessProps) => {
// ...
}
@@ -96,7 +102,7 @@ defineScenario
```
| Generic | Description |
-|:-------------------|:------------------------------------------------------------------------------------------------------|
+| :----------------- | :---------------------------------------------------------------------------------------------------- |
| `PrismaCreateType` | (Optional) the type imported from Prisma's create operation that goes into the "data" key |
| `TName` | (Optional) the name or names of the models in your scenario |
| `TKeys` | (Optional) the key(s) in your scenario. These are really only useful while you write out the scenario |
@@ -129,10 +135,10 @@ defineScenario
This utility type makes it easy for you to access data created by your scenarios in your tests.
It takes three generic parameters:
-| Generic | Description |
-|:--------|:---------------------------------------------------------------------------------|
-| `TData` | The Prisma model that'll be returned |
-| `TName` | (Optional) the name of the model. ("post" in the example below) |
+| Generic | Description |
+| :------ | :------------------------------------------------------------------------------ |
+| `TData` | The Prisma model that'll be returned |
+| `TName` | (Optional) the name of the model. ("post" in the example below) |
| `TKeys` | (optional) the key(s) used to define the scenario. ("one" in the example below) |
We know this is a lot of generics, but that's so you get to choose how specific you want to be with the types!
@@ -157,7 +163,7 @@ You can of course just define the type in the test file instead of importing it.
## DbAuth
-When you setup dbAuth, the generated files in `api/src/lib/auth.ts` and `api/src/functions/auth.ts` have all the types you need. Let's break down some of the utility types.
+When you setup dbAuth, the generated files in `api/src/lib/auth.ts` and `api/src/functions/auth.ts` have all the types you need. Let's break down some of the utility types.
### `DbAuthSession`
@@ -200,21 +206,19 @@ export const handler = async (
context: Context
) => {
// Pass in the generic to the type here 👇
- const forgotPasswordOptions: DbAuthHandlerOptions['forgotPassword'] = {
-
- // ...
-
- // Now in the handler function, `user` will be typed
- handler: (user) => {
- return user
- },
+ const forgotPasswordOptions: DbAuthHandlerOptions['forgotPassword'] =
+ {
+ // ...
- // ...
+ // Now in the handler function, `user` will be typed
+ handler: (user) => {
+ return user
+ },
- }
+ // ...
+ }
// ...
-
}
```
@@ -222,8 +226,8 @@ Note that in strict mode, you'll likely see errors where the handlers expect "tr
## Directives
-
### `ValidatorDirectiveFunc`
+
When you generate a [validator directive](directives.md#validators) you will see your `validate` function typed already with `ValidatorDirectiveFunc`
```ts
@@ -258,10 +262,11 @@ const validate: RequireAuthValidate = ({ directiveArgs }) => {
```
| Generic | Description |
-|:-----------------|:----------------------------------------------------------|
+| :--------------- | :-------------------------------------------------------- |
| `TDirectiveArgs` | The type of arguments passed to your directive in the SDL |
### `TransformerDirectiveFunc`
+
When you generate a [transformer directive](directives.md#transformers) you will see your `transform` function typed with `TransformDirectiveFunc`.
```ts
@@ -275,12 +280,13 @@ This type takes two generics - the type of the field you are transforming, and t
So for example, let's say you have a transformer directive `@maskedEmail(permittedRoles: ['ADMIN'])` that you apply to `String` fields. You would pass in the following types
```ts
-type MaskedEmailTransform = TransformerDirectiveFunc
+type MaskedEmailTransform = TransformerDirectiveFunc<
+ string,
+ { permittedRoles?: string[] }
+>
```
| Generic | Description |
-|:-----------------|:-------------------------------------------------------------------------------|
+| :--------------- | :----------------------------------------------------------------------------- |
| `TField` | This will type `resolvedValue` i.e. the type of the field you are transforming |
| `TDirectiveArgs` | The type of arguments passed to your directive in the SDL |
-
-
diff --git a/docs/docs/vite-configuration.md b/docs/docs/vite-configuration.md
index ce9df4b866f2..d0bd0c125e1b 100644
--- a/docs/docs/vite-configuration.md
+++ b/docs/docs/vite-configuration.md
@@ -12,27 +12,25 @@ So it's worth repeating that you don't have to do any of this, because we config
Regardless, there'll probably come a time when you have to configure Vite. All the Vite configuration for your web side sits in `web/vite.config.{js,ts}`, and can be configured the same as any other Vite project. Let's take a peek!
```js
-import dns from 'dns';
-import { defineConfig } from 'vite';
-import redwood from '@redwoodjs/vite';
+import dns from 'dns'
+import { defineConfig } from 'vite'
+import redwood from '@redwoodjs/vite'
-dns.setDefaultResultOrder('verbatim');
+dns.setDefaultResultOrder('verbatim')
const viteConfig = {
plugins: [
// 👇 this is the RedwoodJS Vite plugin, that houses all the default configuration
- redwood()
+ redwood(),
// ... add any custom Vite plugins you would like here
],
// You can override built in configuration like server, optimizeDeps, etc. here
-};
-export default defineConfig(viteConfig);
-
+}
+export default defineConfig(viteConfig)
```
Checkout Vite's docs on [configuration](https://vitejs.dev/config/)
-
### Sass and Tailwind CSS
Redwood is already configured to use Sass, if the packages are there:
diff --git a/docs/docs/webhooks.md b/docs/docs/webhooks.md
index a185c7ebc4fa..c0c256a4b257 100644
--- a/docs/docs/webhooks.md
+++ b/docs/docs/webhooks.md
@@ -215,6 +215,7 @@ This is a variation on the SHA256 HMAC verification that works with binary buffe
Svix (and by extension, Clerk) gives you a secret token that it uses to create a hash signature with each payload. This hash signature is included with the headers of each request as `svix-signature`.
> Some production environments, like Vercel, might base64 encode the request body string. In that case, the body must be conditionally parsed.
+>
> ```js
> export const handler = async (event: APIGatewayEvent) => {
> const body = event.isBase64Encoded
@@ -658,7 +659,9 @@ export const handler = async (event) => {
}),
}
} else {
- webhookLogger.warn(`Unsupported Orbit Event Type: ${orbitInfo.orbitEventType}`)
+ webhookLogger.warn(
+ `Unsupported Orbit Event Type: ${orbitInfo.orbitEventType}`
+ )
return {
headers: {
'Content-Type': 'application/json',
diff --git a/docs/prettier.config.js b/docs/prettier.config.js
deleted file mode 100644
index 92ac286bf6bf..000000000000
--- a/docs/prettier.config.js
+++ /dev/null
@@ -1,4 +0,0 @@
-module.exports = {
- ...require('../prettier.config'),
- trailingComma: 'es5',
-}
diff --git a/docs/prettier.config.mjs b/docs/prettier.config.mjs
new file mode 100644
index 000000000000..310ad10a937a
--- /dev/null
+++ b/docs/prettier.config.mjs
@@ -0,0 +1,12 @@
+import rootConfig from '../prettier.config.mjs'
+
+/**
+ * @see https://prettier.io/docs/en/configuration.html
+ * @type {import("prettier").Config}
+ */
+const config = {
+ ...rootConfig,
+ trailingComma: 'es5',
+}
+
+export default config
diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css
index 33582ace94f0..5b3ea966d974 100644
--- a/docs/src/css/custom.css
+++ b/docs/src/css/custom.css
@@ -83,7 +83,7 @@
So far just lighten links a little for a11y.
*/
-[data-theme="dark"] {
+[data-theme='dark'] {
--ifm-link-color: var(--teal);
}
@@ -94,9 +94,9 @@
Correct the color on hover and increase the font weight.
*/
-.theme-doc-breadcrumbs{
+.theme-doc-breadcrumbs {
--ifm-link-hover-color: var(--red);
- font-weight: var(--ifm-font-weight-semibold)
+ font-weight: var(--ifm-font-weight-semibold);
}
/*
@@ -150,11 +150,11 @@ blockquote {
padding: 2rem;
}
-[data-theme="light"] blockquote code {
+[data-theme='light'] blockquote code {
--ifm-code-background: hsl(14deg 70% 84%);
}
-[data-theme="dark"] blockquote {
+[data-theme='dark'] blockquote {
background-color: hsl(210deg 3% 15%);
}
@@ -254,11 +254,11 @@ blockquote {
Hide dark mode things on light mode and vice versa.
------------------------
*/
-[data-theme="light"] [data-mode="dark"] {
+[data-theme='light'] [data-mode='dark'] {
display: none;
}
-[data-theme="dark"] [data-mode="light"] {
+[data-theme='dark'] [data-mode='light'] {
display: none;
}
diff --git a/docs/tsconfig.json b/docs/tsconfig.json
index cbd6fdb37d18..a6ba1bd9dd5d 100644
--- a/docs/tsconfig.json
+++ b/docs/tsconfig.json
@@ -1,4 +1,4 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
- "extends": "@docusaurus/tsconfig",
+ "extends": "@docusaurus/tsconfig"
}
diff --git a/docs/versions.json b/docs/versions.json
index 0b2ac7975068..8416f1a461fe 100644
--- a/docs/versions.json
+++ b/docs/versions.json
@@ -1,9 +1 @@
-[
- "7.0",
- "6.x",
- "5.x",
- "4.x",
- "3.x",
- "2.x",
- "1.x"
-]
+["7.0", "6.x", "5.x", "4.x", "3.x", "2.x", "1.x"]
diff --git a/prettier.config.mjs b/prettier.config.mjs
new file mode 100644
index 000000000000..f492b287fba0
--- /dev/null
+++ b/prettier.config.mjs
@@ -0,0 +1,17 @@
+/**
+ * @see https://prettier.io/docs/en/configuration.html
+ * @type {import("prettier").Config}
+ */
+const config = {
+ bracketSpacing: true,
+ tabWidth: 2,
+ semi: false,
+ singleQuote: true,
+ plugins: [
+ 'prettier-plugin-curly',
+ 'prettier-plugin-sh',
+ 'prettier-plugin-packagejson',
+ ],
+}
+
+export default config
From eb68499e741df13e0d076564b06960a598c87a21 Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Fri, 16 Aug 2024 23:28:59 +0100
Subject: [PATCH 36/52] chore(lint): fix prettier configs and ignores (#11295)
So tests started to fail because babel plugin tester uses prettier to
format test output. It looks like it didn't play well with a `.mjs`
config file.
---
.prettierignore | 5 ++++-
docs/{prettier.config.mjs => prettier.config.js} | 7 +++++--
prettier.config.mjs => prettier.config.js | 5 ++++-
3 files changed, 13 insertions(+), 4 deletions(-)
rename docs/{prettier.config.mjs => prettier.config.js} (59%)
rename prettier.config.mjs => prettier.config.js (83%)
diff --git a/.prettierignore b/.prettierignore
index 2a65829322f1..1710316a30fe 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -2,9 +2,11 @@
**/dist
# Ignore fixture projects
-__fixtures__
+/__fixtures__
# Ignore the certain files in /docs
+/docs/.docusaurus
+/docs/build
/docs/versioned_docs
/docs/versioned_sidebars
@@ -21,6 +23,7 @@ packages/create-redwood-rsc-app
# Ignore test fixtures
**/__testfixtures__
**/__tests__/fixtures
+**/__tests__/__fixtures__
# TODO(jgmw): Re-enable these in managable chunks
packages/create-redwood-app/tests/e2e_prompts*
diff --git a/docs/prettier.config.mjs b/docs/prettier.config.js
similarity index 59%
rename from docs/prettier.config.mjs
rename to docs/prettier.config.js
index 310ad10a937a..3678af226708 100644
--- a/docs/prettier.config.mjs
+++ b/docs/prettier.config.js
@@ -1,4 +1,7 @@
-import rootConfig from '../prettier.config.mjs'
+/* eslint-env node */
+// @ts-check
+
+const rootConfig = require('../prettier.config')
/**
* @see https://prettier.io/docs/en/configuration.html
@@ -9,4 +12,4 @@ const config = {
trailingComma: 'es5',
}
-export default config
+module.exports = config
diff --git a/prettier.config.mjs b/prettier.config.js
similarity index 83%
rename from prettier.config.mjs
rename to prettier.config.js
index f492b287fba0..956faa5471a0 100644
--- a/prettier.config.mjs
+++ b/prettier.config.js
@@ -1,3 +1,6 @@
+/* eslint-env node */
+// @ts-check
+
/**
* @see https://prettier.io/docs/en/configuration.html
* @type {import("prettier").Config}
@@ -14,4 +17,4 @@ const config = {
],
}
-export default config
+module.exports = config
From fd56d16472f6bde70028ba303c5409452af1088c Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sat, 17 Aug 2024 00:19:12 +0100
Subject: [PATCH 37/52] feat(context): Build and publish package as dual
esm/cjs (#11294)
Followed the general pattern we've established around our approach to
dual publishing. I only wrote one additional package.json file in the
dist because it felt a little redundant to repeat the one we have at the
root.
We've spoke before about perhaps not wanting to ship two sets of type
definitions - one for cjs and one for esm. I know that they're
essentially the same and for a package like this it almost doubles the
published size. For now let's just ship types for both. In the future
we'll stop publishing cjs anyway.
Since I was just following our established pattern I'm going to go ahead
and merge this in. If we discover something we'd rather change we can
always revert or follow up.
---
packages/context/build.mts | 24 ++++++++++++--
packages/context/package.json | 33 +++++++++++++++++--
packages/context/src/context.ts | 2 +-
.../context/src/global.api-auto-imports.ts | 2 +-
packages/context/src/index.ts | 6 ++--
packages/context/src/store.ts | 2 +-
packages/context/tsconfig.cjs.json | 9 +++++
packages/context/tsconfig.json | 6 ++--
yarn.lock | 3 ++
9 files changed, 75 insertions(+), 12 deletions(-)
create mode 100644 packages/context/tsconfig.cjs.json
diff --git a/packages/context/build.mts b/packages/context/build.mts
index 16175a6725c0..95a1a301b8ea 100644
--- a/packages/context/build.mts
+++ b/packages/context/build.mts
@@ -1,3 +1,23 @@
-import { build } from '@redwoodjs/framework-tools'
+import { writeFileSync } from 'node:fs'
-await build()
+import { build, defaultBuildOptions } from '@redwoodjs/framework-tools'
+
+// ESM build
+await build({
+ buildOptions: {
+ ...defaultBuildOptions,
+ format: 'esm',
+ },
+})
+
+// CJS build
+await build({
+ buildOptions: {
+ ...defaultBuildOptions,
+ outdir: 'dist/cjs',
+ },
+})
+
+// Place a package.json file with `type: commonjs` in the 'dist/cjs' folder so that
+// all files are considered CommonJS modules.
+writeFileSync('dist/cjs/package.json', JSON.stringify({ type: 'commonjs' }))
diff --git a/packages/context/package.json b/packages/context/package.json
index 698298126c94..5e487d1b6d12 100644
--- a/packages/context/package.json
+++ b/packages/context/package.json
@@ -7,7 +7,31 @@
"directory": "packages/context"
},
"license": "MIT",
- "main": "./dist/index.js",
+ "type": "module",
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "default": {
+ "types": "./dist/cjs/index.d.ts",
+ "default": "./dist/cjs/index.js"
+ }
+ },
+ "./dist/store": {
+ "import": {
+ "types": "./dist/store.d.ts",
+ "default": "./dist/store.js"
+ },
+ "default": {
+ "types": "./dist/cjs/store.d.ts",
+ "default": "./dist/cjs/store.js"
+ }
+ }
+ },
+ "main": "./dist/cjs/index.js",
+ "module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
@@ -15,12 +39,17 @@
"scripts": {
"build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-context.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json ./tsconfig.cjs.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build"
},
"devDependencies": {
+ "@arethetypeswrong/cli": "0.15.4",
"@redwoodjs/framework-tools": "workspace:*",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
"tsx": "4.17.0",
"typescript": "5.5.4"
},
diff --git a/packages/context/src/context.ts b/packages/context/src/context.ts
index 85a0fb266261..87a07dc272b7 100644
--- a/packages/context/src/context.ts
+++ b/packages/context/src/context.ts
@@ -1,4 +1,4 @@
-import { getAsyncStoreInstance } from './store'
+import { getAsyncStoreInstance } from './store.js'
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface GlobalContext extends Record {}
diff --git a/packages/context/src/global.api-auto-imports.ts b/packages/context/src/global.api-auto-imports.ts
index cb7507a22fa9..415db112fea5 100644
--- a/packages/context/src/global.api-auto-imports.ts
+++ b/packages/context/src/global.api-auto-imports.ts
@@ -1,4 +1,4 @@
-import type { GlobalContext } from './context'
+import type { GlobalContext } from './context.js'
declare global {
const context: GlobalContext
diff --git a/packages/context/src/index.ts b/packages/context/src/index.ts
index 1f8bd9fe5a85..88ef88991e6d 100644
--- a/packages/context/src/index.ts
+++ b/packages/context/src/index.ts
@@ -1,5 +1,5 @@
-export * from './context'
+export * from './context.js'
// Note: store is not exported here to discourage direct usage.
-import './global.api-auto-imports'
-export * from './global.api-auto-imports'
+import './global.api-auto-imports.js'
+export * from './global.api-auto-imports.js'
diff --git a/packages/context/src/store.ts b/packages/context/src/store.ts
index 934c1b5d5afb..9b472e1eb497 100644
--- a/packages/context/src/store.ts
+++ b/packages/context/src/store.ts
@@ -1,6 +1,6 @@
import { AsyncLocalStorage } from 'async_hooks'
-import type { GlobalContext } from './context'
+import type { GlobalContext } from './context.js'
let CONTEXT_STORAGE: AsyncLocalStorage>
diff --git a/packages/context/tsconfig.cjs.json b/packages/context/tsconfig.cjs.json
new file mode 100644
index 000000000000..213c73d75e6b
--- /dev/null
+++ b/packages/context/tsconfig.cjs.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist/cjs",
+ "module": "CommonJS",
+ "moduleResolution": "Node10",
+ "tsBuildInfoFile": "./tsconfig.cjs.tsbuildinfo"
+ }
+}
diff --git a/packages/context/tsconfig.json b/packages/context/tsconfig.json
index 10dc5ae9f731..db264235028b 100644
--- a/packages/context/tsconfig.json
+++ b/packages/context/tsconfig.json
@@ -2,8 +2,10 @@
"extends": "../../tsconfig.compilerOption.json",
"compilerOptions": {
"rootDir": "src",
- "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"]
}
diff --git a/yarn.lock b/yarn.lock
index a77c81799160..80af0845f642 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7983,7 +7983,10 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/context@workspace:packages/context"
dependencies:
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/framework-tools": "workspace:*"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
languageName: unknown
From b86898af57fae7897aa9d344f1e445fdc0bc59e5 Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sat, 17 Aug 2024 02:43:42 +0100
Subject: [PATCH 38/52] chore(lint): tidy up the prettier ignore (#11297)
As title.
---
.prettierignore | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/.prettierignore b/.prettierignore
index 1710316a30fe..e915df037993 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -14,16 +14,14 @@
/.nx
# Ignore create-redwood-app (we don't want to mismatch between framework and project)
-packages/create-redwood-app
+/packages/create-redwood-app/templates
+/packages/create-redwood-app/tests/**/*.sh
# Ignore create-redwood-rsc-app (it has it's own config)
-packages/create-redwood-rsc-app
+/packages/create-redwood-rsc-app
# TODO(jgmw): Is this too broad?
# Ignore test fixtures
**/__testfixtures__
**/__tests__/fixtures
**/__tests__/__fixtures__
-
-# TODO(jgmw): Re-enable these in managable chunks
-packages/create-redwood-app/tests/e2e_prompts*
From 289cb6249ad8972807b203225c46d98b7edafd36 Mon Sep 17 00:00:00 2001
From: Silas Smith
Date: Sat, 17 Aug 2024 14:19:31 -0700
Subject: [PATCH 39/52] Detect/resolve ambiguous script names (#9848)
Co-authored-by: Tobbe Lundberg
---
.changesets/9848.md | 3 +
.../cli/src/commands/__tests__/exec.test.ts | 38 ++++++++-
packages/cli/src/commands/execHandler.js | 82 ++++++++++++++++---
3 files changed, 107 insertions(+), 16 deletions(-)
create mode 100644 .changesets/9848.md
diff --git a/.changesets/9848.md b/.changesets/9848.md
new file mode 100644
index 000000000000..03ce8329ef77
--- /dev/null
+++ b/.changesets/9848.md
@@ -0,0 +1,3 @@
+- Detect/resolve ambiguous script names (#9848) by @codersmith
+
+Detects and resolves ambiguous script name combinations like `[foo.js, foo.ts]` or `[foo.ts, foo.json]` when running `yarn rw exec foo`.
diff --git a/packages/cli/src/commands/__tests__/exec.test.ts b/packages/cli/src/commands/__tests__/exec.test.ts
index 8a7995bbf2ae..212075565545 100644
--- a/packages/cli/src/commands/__tests__/exec.test.ts
+++ b/packages/cli/src/commands/__tests__/exec.test.ts
@@ -9,13 +9,23 @@ import { handler } from '../execHandler'
vi.mock('envinfo', () => ({ default: { run: () => '' } }))
vi.mock('@redwoodjs/project-config', () => ({
getPaths: () => ({
- scripts: path.join(
- __dirname,
- '../../../../../__fixtures__/test-project/scripts',
- ),
+ scripts: path.join('redwood-app', 'scripts'),
}),
resolveFile: (path: string) => path,
}))
+vi.mock('@redwoodjs/internal/dist/files', () => ({
+ findScripts: () => {
+ const scriptsPath = path.join('redwood-app', 'scripts')
+
+ return [
+ path.join(scriptsPath, 'one', 'two', 'myNestedScript.ts'),
+ path.join(scriptsPath, 'conflicting.js'),
+ path.join(scriptsPath, 'conflicting.ts'),
+ path.join(scriptsPath, 'normalScript.ts'),
+ path.join(scriptsPath, 'secondNormalScript.ts'),
+ ]
+ },
+}))
const mockRedwoodToml = {
fileContents: '',
@@ -49,4 +59,24 @@ describe('yarn rw exec --list', () => {
expect.stringMatching(new RegExp('\\b' + scriptPath + '\\b')),
)
})
+
+ it("does not include the file extension if there's no ambiguity", async () => {
+ await handler({ list: true })
+ expect(vi.mocked(console).log).toHaveBeenCalledWith(
+ expect.stringMatching(new RegExp('\\bnormalScript\\b')),
+ )
+ expect(vi.mocked(console).log).toHaveBeenCalledWith(
+ expect.stringMatching(new RegExp('\\bsecondNormalScript\\b')),
+ )
+ })
+
+ it('includes the file extension if there could be ambiguity', async () => {
+ await handler({ list: true })
+ expect(vi.mocked(console).log).toHaveBeenCalledWith(
+ expect.stringMatching(new RegExp('\\bconflicting.js\\b')),
+ )
+ expect(vi.mocked(console).log).toHaveBeenCalledWith(
+ expect.stringMatching(new RegExp('\\bconflicting.ts\\b')),
+ )
+ })
})
diff --git a/packages/cli/src/commands/execHandler.js b/packages/cli/src/commands/execHandler.js
index f0ae2a84f1d0..dab0cf670a07 100644
--- a/packages/cli/src/commands/execHandler.js
+++ b/packages/cli/src/commands/execHandler.js
@@ -1,4 +1,5 @@
-import path from 'path'
+import fs from 'node:fs'
+import path from 'node:path'
import { context } from '@opentelemetry/api'
import { suppressTracing } from '@opentelemetry/core'
@@ -17,12 +18,30 @@ import { runScriptFunction } from '../lib/exec'
import { generatePrismaClient } from '../lib/generatePrismaClient'
const printAvailableScriptsToConsole = () => {
- console.log('Available scripts:')
- findScripts(getPaths().scripts).forEach((scriptPath) => {
+ // Loop through all scripts and get their relative path
+ // Also group scripts with the same name but different extensions
+ const scripts = findScripts(getPaths().scripts).reduce((acc, scriptPath) => {
const relativePath = path.relative(getPaths().scripts, scriptPath)
- const { ext } = path.parse(relativePath)
- const relativePathWithoutExt = relativePath.slice(0, -ext.length)
- console.log(c.info(`- ${relativePathWithoutExt}`))
+ const ext = path.parse(relativePath).ext
+ const pathNoExt = relativePath.slice(0, -ext.length)
+
+ acc[pathNoExt] ||= []
+ acc[pathNoExt].push(relativePath)
+
+ return acc
+ }, {})
+
+ console.log('Available scripts:')
+ Object.entries(scripts).forEach(([name, paths]) => {
+ // If a script name exists with multiple extensions, print them all,
+ // including the extension
+ if (paths.length > 1) {
+ paths.forEach((scriptPath) => {
+ console.log(c.info(`- ${scriptPath}`))
+ })
+ } else {
+ console.log(c.info(`- ${name}`))
+ }
})
console.log()
}
@@ -40,8 +59,6 @@ export const handler = async (args) => {
return
}
- const scriptPath = path.join(getPaths().scripts, name)
-
const {
overrides: _overrides,
plugins: webPlugins,
@@ -101,11 +118,11 @@ export const handler = async (args) => {
],
})
- try {
- require.resolve(scriptPath)
- } catch {
+ const scriptPath = resolveScriptPath(name)
+
+ if (!scriptPath) {
console.error(
- c.error(`\nNo script called ${c.underline(name)} in ./scripts folder.\n`),
+ c.error(`\nNo script called \`${name}\` in the ./scripts folder.\n`),
)
printAvailableScriptsToConsole()
@@ -145,3 +162,44 @@ export const handler = async (args) => {
await tasks.run()
})
}
+
+function resolveScriptPath(name) {
+ const scriptPath = path.join(getPaths().scripts, name)
+
+ // If scriptPath already has an extension, and it's a valid path, return it
+ // as it is
+ if (fs.existsSync(scriptPath)) {
+ return scriptPath
+ }
+
+ // These extensions match the ones in internal/src/files.ts::findScripts()
+ const extensions = ['.js', '.jsx', '.ts', '.tsx']
+ const matches = []
+
+ for (const extension of extensions) {
+ const p = scriptPath + extension
+
+ if (fs.existsSync(p)) {
+ matches.push(p)
+ }
+ }
+
+ if (matches.length === 1) {
+ return matches[0]
+ } else if (matches.length > 1) {
+ console.error(
+ c.error(
+ `\nMultiple scripts found for \`${name}\`. Please specify the ` +
+ 'extension.',
+ ),
+ )
+
+ matches.forEach((match) => {
+ console.log(c.info(`- ${path.relative(getPaths().scripts, match)}`))
+ })
+
+ process.exit(1)
+ }
+
+ return null
+}
From 7d2ec50f6840cf0f68c638d20bc72c4ea79616cc Mon Sep 17 00:00:00 2001
From: Tobbe Lundberg
Date: Sat, 17 Aug 2024 23:53:31 +0200
Subject: [PATCH 40/52] chore(exec test): Clean up (#11302)
---
.../cli/src/commands/__tests__/exec.test.ts | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
diff --git a/packages/cli/src/commands/__tests__/exec.test.ts b/packages/cli/src/commands/__tests__/exec.test.ts
index 212075565545..9e11c134cef1 100644
--- a/packages/cli/src/commands/__tests__/exec.test.ts
+++ b/packages/cli/src/commands/__tests__/exec.test.ts
@@ -1,18 +1,17 @@
import path from 'node:path'
-import '../../lib/mockTelemetry'
-
import { vi, afterEach, beforeEach, describe, it, expect } from 'vitest'
+import '../../lib/mockTelemetry'
import { handler } from '../execHandler'
-vi.mock('envinfo', () => ({ default: { run: () => '' } }))
vi.mock('@redwoodjs/project-config', () => ({
getPaths: () => ({
scripts: path.join('redwood-app', 'scripts'),
}),
resolveFile: (path: string) => path,
}))
+
vi.mock('@redwoodjs/internal/dist/files', () => ({
findScripts: () => {
const scriptsPath = path.join('redwood-app', 'scripts')
@@ -27,19 +26,6 @@ vi.mock('@redwoodjs/internal/dist/files', () => ({
},
}))
-const mockRedwoodToml = {
- fileContents: '',
-}
-
-// Before rw tests run, api/ and web/ `jest.config.js` is confirmed via existsSync()
-vi.mock('node:fs', async () => ({
- default: {
- readFileSync: () => {
- return mockRedwoodToml.fileContents
- },
- },
-}))
-
beforeEach(() => {
vi.spyOn(console, 'log').mockImplementation(() => {})
})
From 5aa02dd20708f1b0485fc849302348b550e0479f Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sun, 18 Aug 2024 01:08:53 +0100
Subject: [PATCH 41/52] chore(build): build core with esbuild (#11298)
Just looking to switch from using babel to using esbuild.
---
packages/core/.babelrc.js | 1 -
packages/core/build.mts | 10 ++++++++++
packages/core/package.json | 12 +++++++-----
packages/core/src/bins/cross-env.ts | 3 ++-
packages/core/src/bins/eslint.ts | 3 ++-
packages/core/src/bins/jest.ts | 3 ++-
packages/core/src/bins/nodemon.ts | 3 ++-
packages/core/src/bins/redwood.ts | 3 ++-
packages/core/src/bins/rw-api-server-watch.ts | 3 ++-
packages/core/src/bins/rw-dev-fe.ts | 3 ++-
packages/core/src/bins/rw-gen-watch.ts | 3 ++-
packages/core/src/bins/rw-gen.ts | 3 ++-
packages/core/src/bins/rw-log-formatter.ts | 3 ++-
packages/core/src/bins/rw-serve-api.ts | 3 ++-
packages/core/src/bins/rw-serve-fe.ts | 3 ++-
packages/core/src/bins/rw-server.ts | 3 ++-
packages/core/src/bins/rw-web-server.ts | 3 ++-
packages/core/src/bins/rwfw.ts | 3 ++-
yarn.lock | 5 +++--
19 files changed, 50 insertions(+), 23 deletions(-)
delete mode 100644 packages/core/.babelrc.js
create mode 100644 packages/core/build.mts
diff --git a/packages/core/.babelrc.js b/packages/core/.babelrc.js
deleted file mode 100644
index 3b2c815712d9..000000000000
--- a/packages/core/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../babel.config.js' }
diff --git a/packages/core/build.mts b/packages/core/build.mts
new file mode 100644
index 000000000000..05b2602f8d6d
--- /dev/null
+++ b/packages/core/build.mts
@@ -0,0 +1,10 @@
+import { build, defaultBuildOptions } from '@redwoodjs/framework-tools'
+
+// ESM build
+await build({
+ buildOptions: {
+ ...defaultBuildOptions,
+ format: 'esm',
+ outdir: 'dist/bins',
+ },
+})
diff --git a/packages/core/package.json b/packages/core/package.json
index 20c838bc7301..3ca0b51acf43 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -8,6 +8,7 @@
"directory": "packages/core"
},
"license": "MIT",
+ "type": "module",
"bin": {
"cross-env": "./dist/bins/cross-env.js",
"eslint": "./dist/bins/eslint.js",
@@ -29,14 +30,13 @@
"dist"
],
"scripts": {
- "build": "yarn build:js",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\"",
+ "build": "tsx ./build.mts",
"build:pack": "yarn pack -o redwoodjs-core.tgz",
+ "check:package": "yarn publint",
"prepublishOnly": "NODE_ENV=production yarn build"
},
"dependencies": {
"@babel/cli": "7.24.8",
- "@babel/runtime-corejs3": "7.25.0",
"@redwoodjs/cli": "workspace:*",
"@redwoodjs/eslint-config": "workspace:*",
"@redwoodjs/internal": "workspace:*",
@@ -44,7 +44,6 @@
"@redwoodjs/testing": "workspace:*",
"@redwoodjs/web-server": "workspace:*",
"babel-loader": "^9.1.3",
- "core-js": "3.38.0",
"graphql-tag": "2.12.6",
"lodash": "4.17.21",
"mini-css-extract-plugin": "2.9.0",
@@ -57,7 +56,10 @@
"url-loader": "4.1.1"
},
"devDependencies": {
- "@types/lodash": "4.17.7"
+ "@redwoodjs/framework-tools": "workspace:*",
+ "@types/lodash": "4.17.7",
+ "publint": "0.2.10",
+ "tsx": "4.17.0"
},
"gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1"
}
diff --git a/packages/core/src/bins/cross-env.ts b/packages/core/src/bins/cross-env.ts
index 7b9c151ce72b..f7c82c956575 100644
--- a/packages/core/src/bins/cross-env.ts
+++ b/packages/core/src/bins/cross-env.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromCrossEnv = createRequire(
require.resolve('cross-env/package.json'),
)
diff --git a/packages/core/src/bins/eslint.ts b/packages/core/src/bins/eslint.ts
index a4904d38e678..0c147901ccdf 100644
--- a/packages/core/src/bins/eslint.ts
+++ b/packages/core/src/bins/eslint.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromESLint = createRequire(require.resolve('eslint/package.json'))
const bins = requireFromESLint('./package.json')['bin']
diff --git a/packages/core/src/bins/jest.ts b/packages/core/src/bins/jest.ts
index 7269056f094a..ceaac11fb337 100644
--- a/packages/core/src/bins/jest.ts
+++ b/packages/core/src/bins/jest.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromJest = createRequire(require.resolve('jest/package.json'))
const bin = requireFromJest('./package.json')['bin']
diff --git a/packages/core/src/bins/nodemon.ts b/packages/core/src/bins/nodemon.ts
index 6b6543e29bf3..f754664b5767 100644
--- a/packages/core/src/bins/nodemon.ts
+++ b/packages/core/src/bins/nodemon.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromNodemon = createRequire(
require.resolve('nodemon/package.json'),
)
diff --git a/packages/core/src/bins/redwood.ts b/packages/core/src/bins/redwood.ts
index f5fc8a995c02..84d3bcbd1e47 100644
--- a/packages/core/src/bins/redwood.ts
+++ b/packages/core/src/bins/redwood.ts
@@ -17,13 +17,14 @@
* - https://yarnpkg.com/advanced/rulebook#packages-should-only-ever-require-what-they-formally-list-in-their-dependencies
* - https://yarnpkg.com/advanced/rulebook#modules-shouldnt-hardcode-node_modules-paths-to-access-other-modules
*/
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
// You can think about the argument we're passing to `createRequire` as being kinda like setting the `cwd`:
//
// > It's using the path/URL to resolve relative paths (e.g.: createRequire('/foo/bar')('./baz') may load /foo/baz/index.js)
//
// See https://github.com/nodejs/node/issues/40567#issuecomment-949825461.
+const require = createRequire(import.meta.url)
const requireFromCli = createRequire(
require.resolve('@redwoodjs/cli/package.json'),
)
diff --git a/packages/core/src/bins/rw-api-server-watch.ts b/packages/core/src/bins/rw-api-server-watch.ts
index 6a42ab7ecd22..1e23bbc56f27 100644
--- a/packages/core/src/bins/rw-api-server-watch.ts
+++ b/packages/core/src/bins/rw-api-server-watch.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromApiServer = createRequire(
require.resolve('@redwoodjs/api-server/package.json'),
)
diff --git a/packages/core/src/bins/rw-dev-fe.ts b/packages/core/src/bins/rw-dev-fe.ts
index ecd5fd1b9138..947ad547277f 100644
--- a/packages/core/src/bins/rw-dev-fe.ts
+++ b/packages/core/src/bins/rw-dev-fe.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromRwVite = createRequire(
require.resolve('@redwoodjs/vite/package.json'),
)
diff --git a/packages/core/src/bins/rw-gen-watch.ts b/packages/core/src/bins/rw-gen-watch.ts
index 26ea5dcdb58d..b8b90d86aeb6 100644
--- a/packages/core/src/bins/rw-gen-watch.ts
+++ b/packages/core/src/bins/rw-gen-watch.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromInternal = createRequire(
require.resolve('@redwoodjs/internal/package.json'),
)
diff --git a/packages/core/src/bins/rw-gen.ts b/packages/core/src/bins/rw-gen.ts
index 35dc82d752be..a22e8ccc389b 100644
--- a/packages/core/src/bins/rw-gen.ts
+++ b/packages/core/src/bins/rw-gen.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromInternal = createRequire(
require.resolve('@redwoodjs/internal/package.json'),
)
diff --git a/packages/core/src/bins/rw-log-formatter.ts b/packages/core/src/bins/rw-log-formatter.ts
index df9917a8db3e..a4021baa6afd 100644
--- a/packages/core/src/bins/rw-log-formatter.ts
+++ b/packages/core/src/bins/rw-log-formatter.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromApiServer = createRequire(
require.resolve('@redwoodjs/api-server/package.json'),
)
diff --git a/packages/core/src/bins/rw-serve-api.ts b/packages/core/src/bins/rw-serve-api.ts
index 43a2de0478c5..dfb47d9c79c1 100644
--- a/packages/core/src/bins/rw-serve-api.ts
+++ b/packages/core/src/bins/rw-serve-api.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromApiServer = createRequire(
require.resolve('@redwoodjs/api-server/package.json'),
)
diff --git a/packages/core/src/bins/rw-serve-fe.ts b/packages/core/src/bins/rw-serve-fe.ts
index bc9497e49fea..30dda5fe24e1 100644
--- a/packages/core/src/bins/rw-serve-fe.ts
+++ b/packages/core/src/bins/rw-serve-fe.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromRwVite = createRequire(
require.resolve('@redwoodjs/vite/package.json'),
)
diff --git a/packages/core/src/bins/rw-server.ts b/packages/core/src/bins/rw-server.ts
index 6de88f63bb14..0ea4296ef6ba 100644
--- a/packages/core/src/bins/rw-server.ts
+++ b/packages/core/src/bins/rw-server.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromApiServer = createRequire(
require.resolve('@redwoodjs/api-server/package.json'),
)
diff --git a/packages/core/src/bins/rw-web-server.ts b/packages/core/src/bins/rw-web-server.ts
index 4e85da8a0936..0ad370457265 100644
--- a/packages/core/src/bins/rw-web-server.ts
+++ b/packages/core/src/bins/rw-web-server.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromWebServer = createRequire(
require.resolve('@redwoodjs/web-server/package.json'),
)
diff --git a/packages/core/src/bins/rwfw.ts b/packages/core/src/bins/rwfw.ts
index 5d09e6d133ef..2d708787c918 100644
--- a/packages/core/src/bins/rwfw.ts
+++ b/packages/core/src/bins/rwfw.ts
@@ -1,6 +1,7 @@
#!/usr/bin/env node
-import { createRequire } from 'module'
+import { createRequire } from 'node:module'
+const require = createRequire(import.meta.url)
const requireFromCli = createRequire(
require.resolve('@redwoodjs/cli/package.json'),
)
diff --git a/yarn.lock b/yarn.lock
index 80af0845f642..efd1a6b3691b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8012,24 +8012,25 @@ __metadata:
resolution: "@redwoodjs/core@workspace:packages/core"
dependencies:
"@babel/cli": "npm:7.24.8"
- "@babel/runtime-corejs3": "npm:7.25.0"
"@redwoodjs/cli": "workspace:*"
"@redwoodjs/eslint-config": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@redwoodjs/internal": "workspace:*"
"@redwoodjs/project-config": "workspace:*"
"@redwoodjs/testing": "workspace:*"
"@redwoodjs/web-server": "workspace:*"
"@types/lodash": "npm:4.17.7"
babel-loader: "npm:^9.1.3"
- core-js: "npm:3.38.0"
graphql-tag: "npm:2.12.6"
lodash: "npm:4.17.21"
mini-css-extract-plugin: "npm:2.9.0"
nodemon: "npm:3.1.4"
null-loader: "npm:4.0.1"
+ publint: "npm:0.2.10"
react-refresh: "npm:0.14.0"
resolve-url-loader: "npm:5.0.0"
rimraf: "npm:6.0.1"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
url-loader: "npm:4.1.1"
bin:
From 9fb2d6b74e1a3a55b76afc2fe04230463e22ff47 Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sun, 18 Aug 2024 01:29:41 +0100
Subject: [PATCH 42/52] chore(deps): Remove webpack related dependencies
(#11299)
Looks like these are some webpack specific dependencies we don't need
anymore.
---
packages/core/package.json | 7 +-
yarn.lock | 264 +++----------------------------------
2 files changed, 16 insertions(+), 255 deletions(-)
diff --git a/packages/core/package.json b/packages/core/package.json
index 3ca0b51acf43..b3c378ca5e40 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -43,17 +43,12 @@
"@redwoodjs/project-config": "workspace:*",
"@redwoodjs/testing": "workspace:*",
"@redwoodjs/web-server": "workspace:*",
- "babel-loader": "^9.1.3",
"graphql-tag": "2.12.6",
"lodash": "4.17.21",
- "mini-css-extract-plugin": "2.9.0",
"nodemon": "3.1.4",
- "null-loader": "4.0.1",
"react-refresh": "0.14.0",
- "resolve-url-loader": "5.0.0",
"rimraf": "6.0.1",
- "typescript": "5.5.4",
- "url-loader": "4.1.1"
+ "typescript": "5.5.4"
},
"devDependencies": {
"@redwoodjs/framework-tools": "workspace:*",
diff --git a/yarn.lock b/yarn.lock
index efd1a6b3691b..32dec80ebcd6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8020,19 +8020,14 @@ __metadata:
"@redwoodjs/testing": "workspace:*"
"@redwoodjs/web-server": "workspace:*"
"@types/lodash": "npm:4.17.7"
- babel-loader: "npm:^9.1.3"
graphql-tag: "npm:2.12.6"
lodash: "npm:4.17.21"
- mini-css-extract-plugin: "npm:2.9.0"
nodemon: "npm:3.1.4"
- null-loader: "npm:4.0.1"
publint: "npm:0.2.10"
react-refresh: "npm:0.14.0"
- resolve-url-loader: "npm:5.0.0"
rimraf: "npm:6.0.1"
tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
- url-loader: "npm:4.1.1"
bin:
cross-env: ./dist/bins/cross-env.js
eslint: ./dist/bins/eslint.js
@@ -10726,7 +10721,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9":
+"@types/json-schema@npm:*":
version: 7.0.15
resolution: "@types/json-schema@npm:7.0.15"
checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db
@@ -11830,16 +11825,6 @@ __metadata:
languageName: node
linkType: hard
-"adjust-sourcemap-loader@npm:^4.0.0":
- version: 4.0.0
- resolution: "adjust-sourcemap-loader@npm:4.0.0"
- dependencies:
- loader-utils: "npm:^2.0.0"
- regex-parser: "npm:^2.2.11"
- checksum: 10c0/6a6e5bb8b670e4e1238c708f6163e92aa2ad0308fe5913de73c89e4cbf41738ee0bcc5552b94d0b7bf8be435ee49b78c6de8a6db7badd80762051e843c8aa14f
- languageName: node
- linkType: hard
-
"agent-base@npm:5":
version: 5.1.1
resolution: "agent-base@npm:5.1.1"
@@ -11900,27 +11885,7 @@ __metadata:
languageName: node
linkType: hard
-"ajv-keywords@npm:^3.5.2":
- version: 3.5.2
- resolution: "ajv-keywords@npm:3.5.2"
- peerDependencies:
- ajv: ^6.9.1
- checksum: 10c0/0c57a47cbd656e8cdfd99d7c2264de5868918ffa207c8d7a72a7f63379d4333254b2ba03d69e3c035e996a3fd3eb6d5725d7a1597cca10694296e32510546360
- languageName: node
- linkType: hard
-
-"ajv-keywords@npm:^5.1.0":
- version: 5.1.0
- resolution: "ajv-keywords@npm:5.1.0"
- dependencies:
- fast-deep-equal: "npm:^3.1.3"
- peerDependencies:
- ajv: ^8.8.2
- checksum: 10c0/18bec51f0171b83123ba1d8883c126e60c6f420cef885250898bf77a8d3e65e3bfb9e8564f497e30bdbe762a83e0d144a36931328616a973ee669dc74d4a9590
- languageName: node
- linkType: hard
-
-"ajv@npm:8.17.1, ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0, ajv@npm:^8.9.0":
+"ajv@npm:8.17.1, ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0":
version: 8.17.1
resolution: "ajv@npm:8.17.1"
dependencies:
@@ -11932,7 +11897,7 @@ __metadata:
languageName: node
linkType: hard
-"ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.12.6":
+"ajv@npm:^6.12.4, ajv@npm:^6.12.6":
version: 6.12.6
resolution: "ajv@npm:6.12.6"
dependencies:
@@ -12641,19 +12606,6 @@ __metadata:
languageName: node
linkType: hard
-"babel-loader@npm:^9.1.3":
- version: 9.1.3
- resolution: "babel-loader@npm:9.1.3"
- dependencies:
- find-cache-dir: "npm:^4.0.0"
- schema-utils: "npm:^4.0.0"
- peerDependencies:
- "@babel/core": ^7.12.0
- webpack: ">=5"
- checksum: 10c0/e3fc3c9e02bd908b37e8e8cd4f3d7280cf6ac45e33fc203aedbb615135a0fecc33bf92573b71a166a827af029d302c0b060354985cd91d510320bd70a2f949eb
- languageName: node
- linkType: hard
-
"babel-plugin-auto-import@npm:1.1.0":
version: 1.1.0
resolution: "babel-plugin-auto-import@npm:1.1.0"
@@ -12913,13 +12865,6 @@ __metadata:
languageName: node
linkType: hard
-"big.js@npm:^5.2.2":
- version: 5.2.2
- resolution: "big.js@npm:5.2.2"
- checksum: 10c0/230520f1ff920b2d2ce3e372d77a33faa4fa60d802fe01ca4ffbc321ee06023fe9a741ac02793ee778040a16b7e497f7d60c504d1c402b8fdab6f03bb785a25f
- languageName: node
- linkType: hard
-
"bignumber.js@npm:^9.0.0":
version: 9.1.1
resolution: "bignumber.js@npm:9.1.1"
@@ -14097,13 +14042,6 @@ __metadata:
languageName: node
linkType: hard
-"common-path-prefix@npm:^3.0.0":
- version: 3.0.0
- resolution: "common-path-prefix@npm:3.0.0"
- checksum: 10c0/c4a74294e1b1570f4a8ab435285d185a03976c323caa16359053e749db4fde44e3e6586c29cd051100335e11895767cbbd27ea389108e327d62f38daf4548fdb
- languageName: node
- linkType: hard
-
"common-tags@npm:1.8.2, common-tags@npm:^1.8.0":
version: 1.8.2
resolution: "common-tags@npm:1.8.2"
@@ -14388,7 +14326,7 @@ __metadata:
languageName: node
linkType: hard
-"convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0":
+"convert-source-map@npm:^1.6.0":
version: 1.9.0
resolution: "convert-source-map@npm:1.9.0"
checksum: 10c0/281da55454bf8126cbc6625385928c43479f2060984180c42f3a86c8b8c12720a24eac260624a7d1e090004028d2dee78602330578ceec1a08e27cb8bb0a8a5b
@@ -15795,13 +15733,6 @@ __metadata:
languageName: node
linkType: hard
-"emojis-list@npm:^3.0.0":
- version: 3.0.0
- resolution: "emojis-list@npm:3.0.0"
- checksum: 10c0/7dc4394b7b910444910ad64b812392159a21e1a7ecc637c775a440227dcb4f80eff7fe61f4453a7d7603fa23d23d30cc93fe9e4b5ed985b88d6441cd4a35117b
- languageName: node
- linkType: hard
-
"encodeurl@npm:~1.0.2":
version: 1.0.2
resolution: "encodeurl@npm:1.0.2"
@@ -17399,16 +17330,6 @@ __metadata:
languageName: node
linkType: hard
-"find-cache-dir@npm:^4.0.0":
- version: 4.0.0
- resolution: "find-cache-dir@npm:4.0.0"
- dependencies:
- common-path-prefix: "npm:^3.0.0"
- pkg-dir: "npm:^7.0.0"
- checksum: 10c0/0faa7956974726c8769671de696d24c643ca1e5b8f7a2401283caa9e07a5da093293e0a0f4bd18c920ec981d2ef945c7f5b946cde268dfc9077d833ad0293cff
- languageName: node
- linkType: hard
-
"find-my-way@npm:8.2.0, find-my-way@npm:^8.0.0":
version: 8.2.0
resolution: "find-my-way@npm:8.2.0"
@@ -17458,16 +17379,6 @@ __metadata:
languageName: node
linkType: hard
-"find-up@npm:^6.3.0":
- version: 6.3.0
- resolution: "find-up@npm:6.3.0"
- dependencies:
- locate-path: "npm:^7.1.0"
- path-exists: "npm:^5.0.0"
- checksum: 10c0/07e0314362d316b2b13f7f11ea4692d5191e718ca3f7264110127520f3347996349bf9e16805abae3e196805814bc66ef4bff2b8904dc4a6476085fc9b0eba07
- languageName: node
- linkType: hard
-
"firebase-admin@npm:12.3.1":
version: 12.3.1
resolution: "firebase-admin@npm:12.3.1"
@@ -20969,7 +20880,7 @@ __metadata:
languageName: node
linkType: hard
-"json5@npm:2.2.3, json5@npm:^2.1.2, json5@npm:^2.2.2, json5@npm:^2.2.3":
+"json5@npm:2.2.3, json5@npm:^2.2.2, json5@npm:^2.2.3":
version: 2.2.3
resolution: "json5@npm:2.2.3"
bin:
@@ -21528,17 +21439,6 @@ __metadata:
languageName: node
linkType: hard
-"loader-utils@npm:^2.0.0":
- version: 2.0.4
- resolution: "loader-utils@npm:2.0.4"
- dependencies:
- big.js: "npm:^5.2.2"
- emojis-list: "npm:^3.0.0"
- json5: "npm:^2.1.2"
- checksum: 10c0/d5654a77f9d339ec2a03d88221a5a695f337bf71eb8dea031b3223420bb818964ba8ed0069145c19b095f6c8b8fd386e602a3fc7ca987042bd8bb1dcc90d7100
- languageName: node
- linkType: hard
-
"local-pkg@npm:^0.5.0":
version: 0.5.0
resolution: "local-pkg@npm:0.5.0"
@@ -21587,15 +21487,6 @@ __metadata:
languageName: node
linkType: hard
-"locate-path@npm:^7.1.0":
- version: 7.2.0
- resolution: "locate-path@npm:7.2.0"
- dependencies:
- p-locate: "npm:^6.0.0"
- checksum: 10c0/139e8a7fe11cfbd7f20db03923cacfa5db9e14fa14887ea121345597472b4a63c1a42a8a5187defeeff6acf98fd568da7382aa39682d38f0af27433953a97751
- languageName: node
- linkType: hard
-
"lodash-decorators@npm:6.0.1":
version: 6.0.1
resolution: "lodash-decorators@npm:6.0.1"
@@ -22304,7 +22195,7 @@ __metadata:
languageName: node
linkType: hard
-"mime-types@npm:2.1.35, mime-types@npm:^2.1.12, mime-types@npm:^2.1.25, mime-types@npm:^2.1.27, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
+"mime-types@npm:2.1.35, mime-types@npm:^2.1.12, mime-types@npm:^2.1.25, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
version: 2.1.35
resolution: "mime-types@npm:2.1.35"
dependencies:
@@ -22375,18 +22266,6 @@ __metadata:
languageName: node
linkType: hard
-"mini-css-extract-plugin@npm:2.9.0":
- version: 2.9.0
- resolution: "mini-css-extract-plugin@npm:2.9.0"
- dependencies:
- schema-utils: "npm:^4.0.0"
- tapable: "npm:^2.2.1"
- peerDependencies:
- webpack: ^5.0.0
- checksum: 10c0/46e20747ea250420db8a82801b9779299ce3cd5ec4d6dd75e00904c39cc80f0f01decaa534b8cb9658d7d3b656b919cb2cc84b1ba7e2394d2d6548578a5c2901
- languageName: node
- linkType: hard
-
"minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1":
version: 1.0.1
resolution: "minimalistic-assert@npm:1.0.1"
@@ -23688,18 +23567,6 @@ __metadata:
languageName: node
linkType: hard
-"null-loader@npm:4.0.1":
- version: 4.0.1
- resolution: "null-loader@npm:4.0.1"
- dependencies:
- loader-utils: "npm:^2.0.0"
- schema-utils: "npm:^3.0.0"
- peerDependencies:
- webpack: ^4.0.0 || ^5.0.0
- checksum: 10c0/fe9a74a928c9ddc1eab7be0e4322516439562d6efd6feeb0f7c61777d4b79a6a8e5a6bc8133deb59408f3f423bdf84c154a88168154a583154e9e33d544b4d42
- languageName: node
- linkType: hard
-
"nullthrows@npm:^1.1.1":
version: 1.1.1
resolution: "nullthrows@npm:1.1.1"
@@ -24121,15 +23988,6 @@ __metadata:
languageName: node
linkType: hard
-"p-limit@npm:^4.0.0":
- version: 4.0.0
- resolution: "p-limit@npm:4.0.0"
- dependencies:
- yocto-queue: "npm:^1.0.0"
- checksum: 10c0/a56af34a77f8df2ff61ddfb29431044557fcbcb7642d5a3233143ebba805fc7306ac1d448de724352861cb99de934bc9ab74f0d16fe6a5460bdbdf938de875ad
- languageName: node
- linkType: hard
-
"p-locate@npm:^2.0.0":
version: 2.0.0
resolution: "p-locate@npm:2.0.0"
@@ -24166,15 +24024,6 @@ __metadata:
languageName: node
linkType: hard
-"p-locate@npm:^6.0.0":
- version: 6.0.0
- resolution: "p-locate@npm:6.0.0"
- dependencies:
- p-limit: "npm:^4.0.0"
- checksum: 10c0/d72fa2f41adce59c198270aa4d3c832536c87a1806e0f69dffb7c1a7ca998fb053915ca833d90f166a8c082d3859eabfed95f01698a3214c20df6bb8de046312
- languageName: node
- linkType: hard
-
"p-map-series@npm:2.1.0":
version: 2.1.0
resolution: "p-map-series@npm:2.1.0"
@@ -24541,13 +24390,6 @@ __metadata:
languageName: node
linkType: hard
-"path-exists@npm:^5.0.0":
- version: 5.0.0
- resolution: "path-exists@npm:5.0.0"
- checksum: 10c0/b170f3060b31604cde93eefdb7392b89d832dfbc1bed717c9718cbe0f230c1669b7e75f87e19901da2250b84d092989a0f9e44d2ef41deb09aa3ad28e691a40a
- languageName: node
- linkType: hard
-
"path-is-absolute@npm:^1.0.0":
version: 1.0.1
resolution: "path-is-absolute@npm:1.0.1"
@@ -24831,15 +24673,6 @@ __metadata:
languageName: node
linkType: hard
-"pkg-dir@npm:^7.0.0":
- version: 7.0.0
- resolution: "pkg-dir@npm:7.0.0"
- dependencies:
- find-up: "npm:^6.3.0"
- checksum: 10c0/1afb23d2efb1ec9d8b2c4a0c37bf146822ad2774f074cb05b853be5dca1b40815c5960dd126df30ab8908349262a266f31b771e877235870a3b8fd313beebec5
- languageName: node
- linkType: hard
-
"pkg-types@npm:^1.0.3, pkg-types@npm:^1.1.1, pkg-types@npm:^1.1.3":
version: 1.1.3
resolution: "pkg-types@npm:1.1.3"
@@ -24928,7 +24761,7 @@ __metadata:
languageName: node
linkType: hard
-"postcss@npm:^8.2.14, postcss@npm:^8.4.41":
+"postcss@npm:^8.4.41":
version: 8.4.41
resolution: "postcss@npm:8.4.41"
dependencies:
@@ -26089,13 +25922,6 @@ __metadata:
languageName: node
linkType: hard
-"regex-parser@npm:^2.2.11":
- version: 2.2.11
- resolution: "regex-parser@npm:2.2.11"
- checksum: 10c0/6572acbd46b5444215a73cf164f3c6fdbd73b8a2cde6a31a97307e514d20f5cbb8609f9e4994a7744207f2d1bf9e6fca4bbc0c9854f2b3da77ae0063efdc3f98
- languageName: node
- linkType: hard
-
"regexp-to-ast@npm:0.5.0":
version: 0.5.0
resolution: "regexp-to-ast@npm:0.5.0"
@@ -26335,19 +26161,6 @@ __metadata:
languageName: node
linkType: hard
-"resolve-url-loader@npm:5.0.0":
- version: 5.0.0
- resolution: "resolve-url-loader@npm:5.0.0"
- dependencies:
- adjust-sourcemap-loader: "npm:^4.0.0"
- convert-source-map: "npm:^1.7.0"
- loader-utils: "npm:^2.0.0"
- postcss: "npm:^8.2.14"
- source-map: "npm:0.6.1"
- checksum: 10c0/53eef3620332f2fc35a4deffaa4395064b2ffd1bc28be380faa3f1e99c2fb7bbf0f705700b4539387d5b6c39586df54a92cd5d031606f19de4bf9e0ff1b6a522
- languageName: node
- linkType: hard
-
"resolve.exports@npm:^2.0.0":
version: 2.0.2
resolution: "resolve.exports@npm:2.0.2"
@@ -26866,29 +26679,6 @@ __metadata:
languageName: node
linkType: hard
-"schema-utils@npm:^3.0.0":
- version: 3.3.0
- resolution: "schema-utils@npm:3.3.0"
- dependencies:
- "@types/json-schema": "npm:^7.0.8"
- ajv: "npm:^6.12.5"
- ajv-keywords: "npm:^3.5.2"
- checksum: 10c0/fafdbde91ad8aa1316bc543d4b61e65ea86970aebbfb750bfb6d8a6c287a23e415e0e926c2498696b242f63af1aab8e585252637fabe811fd37b604351da6500
- languageName: node
- linkType: hard
-
-"schema-utils@npm:^4.0.0":
- version: 4.2.0
- resolution: "schema-utils@npm:4.2.0"
- dependencies:
- "@types/json-schema": "npm:^7.0.9"
- ajv: "npm:^8.9.0"
- ajv-formats: "npm:^2.1.1"
- ajv-keywords: "npm:^5.1.0"
- checksum: 10c0/8dab7e7800316387fd8569870b4b668cfcecf95ac551e369ea799bbcbfb63fb0365366d4b59f64822c9f7904d8c5afcfaf5a6124a4b08783e558cd25f299a6b4
- languageName: node
- linkType: hard
-
"scuid@npm:^1.1.0":
version: 1.1.0
resolution: "scuid@npm:1.1.0"
@@ -27404,13 +27194,6 @@ __metadata:
languageName: node
linkType: hard
-"source-map@npm:0.6.1, source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1":
- version: 0.6.1
- resolution: "source-map@npm:0.6.1"
- checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011
- languageName: node
- linkType: hard
-
"source-map@npm:0.7.4":
version: 0.7.4
resolution: "source-map@npm:0.7.4"
@@ -27418,6 +27201,13 @@ __metadata:
languageName: node
linkType: hard
+"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1":
+ version: 0.6.1
+ resolution: "source-map@npm:0.6.1"
+ checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011
+ languageName: node
+ linkType: hard
+
"space-separated-tokens@npm:^1.0.0":
version: 1.1.5
resolution: "space-separated-tokens@npm:1.1.5"
@@ -28148,7 +27938,7 @@ __metadata:
languageName: node
linkType: hard
-"tapable@npm:^2.2.0, tapable@npm:^2.2.1":
+"tapable@npm:^2.2.0":
version: 2.2.1
resolution: "tapable@npm:2.2.1"
checksum: 10c0/bc40e6efe1e554d075469cedaba69a30eeb373552aaf41caeaaa45bf56ffacc2674261b106245bd566b35d8f3329b52d838e851ee0a852120acae26e622925c9
@@ -29385,23 +29175,6 @@ __metadata:
languageName: node
linkType: hard
-"url-loader@npm:4.1.1":
- version: 4.1.1
- resolution: "url-loader@npm:4.1.1"
- dependencies:
- loader-utils: "npm:^2.0.0"
- mime-types: "npm:^2.1.27"
- schema-utils: "npm:^3.0.0"
- peerDependencies:
- file-loader: "*"
- webpack: ^4.0.0 || ^5.0.0
- peerDependenciesMeta:
- file-loader:
- optional: true
- checksum: 10c0/71b6300e02ce26c70625eae1a2297c0737635038c62691bb3007ac33e85c0130efc74bfb444baf5c6b3bad5953491159d31d66498967d1417865d0c7e7cd1a64
- languageName: node
- linkType: hard
-
"url-parse-lax@npm:^3.0.0":
version: 3.0.0
resolution: "url-parse-lax@npm:3.0.0"
@@ -30505,13 +30278,6 @@ __metadata:
languageName: node
linkType: hard
-"yocto-queue@npm:^1.0.0":
- version: 1.0.0
- resolution: "yocto-queue@npm:1.0.0"
- checksum: 10c0/856117aa15cf5103d2a2fb173f0ab4acb12b4b4d0ed3ab249fdbbf612e55d1cadfd27a6110940e24746fb0a78cf640b522cc8bca76f30a3b00b66e90cf82abe0
- languageName: node
- linkType: hard
-
"yoga-layout-prebuilt@npm:1.10.0":
version: 1.10.0
resolution: "yoga-layout-prebuilt@npm:1.10.0"
From 78b897a4b689e9cf1f336113b1fbf18bf6dd871d Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sun, 18 Aug 2024 01:56:27 +0100
Subject: [PATCH 43/52] fix(auth-providers): Move away from babel for building
'setup' packages (#11303)
This switches from babel to esbuild for building the 'api' auth provider
packages. Continuing the work of #11301 but this time for the setup
packages.
I have not included the dbAuth package deliberately and will follow up
with a separate update to that package.
I had to make a small change to the `@redwoodjs/cli-helpers` package.
This was because tsc was not happy that I was statically importing from
a package marked `type: module` from a package marked as `type:
commonjs` because it would result in requiring an es module. It was not
understanding that the cli-helpers package was dual cjs/esm and that it
will be totally fine to either import or require from it. The solution
(it appears) is to be specific in the exports field of the package.json.
Those edits here made tsc happy to generate types and didn't throw the
error.
I didn't update the styling of the tsconfig when I changed the
cli-helpers package. We should refactor that standalone. I'm really not
at all thrilled about my recent changes which use the `moduleResolution:
node` because that isn't what we should be using but tsc errors out
based on your `module` and `moduleResolution` setting which has been a
pain. This for now is okay and we can revisit once we encounter the
issue of using two different module resolution algorithms depending on
cjs vs esm.
---
.../auth-providers/auth0/setup/.babelrc.js | 1 -
packages/auth-providers/auth0/setup/build.mts | 8 +++
.../auth-providers/auth0/setup/package.json | 33 ++++++---
.../auth-providers/auth0/setup/tsconfig.json | 5 +-
.../azureActiveDirectory/setup/.babelrc.js | 1 -
.../azureActiveDirectory/setup/build.mts | 8 +++
.../azureActiveDirectory/setup/package.json | 33 ++++++---
.../azureActiveDirectory/setup/tsconfig.json | 5 +-
.../auth-providers/clerk/setup/.babelrc.js | 1 -
packages/auth-providers/clerk/setup/build.mts | 8 +++
.../auth-providers/clerk/setup/package.json | 33 ++++++---
.../auth-providers/clerk/setup/tsconfig.json | 5 +-
.../auth-providers/custom/setup/.babelrc.js | 1 -
.../auth-providers/custom/setup/build.mts | 8 +++
.../auth-providers/custom/setup/package.json | 33 ++++++---
.../auth-providers/custom/setup/tsconfig.json | 5 +-
.../auth-providers/firebase/setup/.babelrc.js | 1 -
.../auth-providers/firebase/setup/build.mts | 8 +++
.../firebase/setup/package.json | 33 ++++++---
.../firebase/setup/tsconfig.json | 5 +-
.../auth-providers/netlify/setup/.babelrc.js | 1 -
.../auth-providers/netlify/setup/build.mts | 8 +++
.../auth-providers/netlify/setup/package.json | 33 ++++++---
.../netlify/setup/tsconfig.json | 5 +-
.../auth-providers/supabase/setup/.babelrc.js | 1 -
.../auth-providers/supabase/setup/build.mts | 8 +++
.../supabase/setup/package.json | 33 ++++++---
.../supabase/setup/tsconfig.json | 5 +-
.../supertokens/setup/.babelrc.js | 1 -
.../supertokens/setup/build.mts | 8 +++
.../supertokens/setup/package.json | 33 ++++++---
.../supertokens/setup/tsconfig.json | 5 +-
packages/cli-helpers/package.json | 35 ++++++---
packages/cli-helpers/tsconfig.build.cjs.json | 9 +++
packages/framework-tools/src/buildDefaults.ts | 44 ++++++++++++
yarn.lock | 72 ++++++++++---------
36 files changed, 414 insertions(+), 122 deletions(-)
delete mode 100644 packages/auth-providers/auth0/setup/.babelrc.js
create mode 100644 packages/auth-providers/auth0/setup/build.mts
delete mode 100644 packages/auth-providers/azureActiveDirectory/setup/.babelrc.js
create mode 100644 packages/auth-providers/azureActiveDirectory/setup/build.mts
delete mode 100644 packages/auth-providers/clerk/setup/.babelrc.js
create mode 100644 packages/auth-providers/clerk/setup/build.mts
delete mode 100644 packages/auth-providers/custom/setup/.babelrc.js
create mode 100644 packages/auth-providers/custom/setup/build.mts
delete mode 100644 packages/auth-providers/firebase/setup/.babelrc.js
create mode 100644 packages/auth-providers/firebase/setup/build.mts
delete mode 100644 packages/auth-providers/netlify/setup/.babelrc.js
create mode 100644 packages/auth-providers/netlify/setup/build.mts
delete mode 100644 packages/auth-providers/supabase/setup/.babelrc.js
create mode 100644 packages/auth-providers/supabase/setup/build.mts
delete mode 100644 packages/auth-providers/supertokens/setup/.babelrc.js
create mode 100644 packages/auth-providers/supertokens/setup/build.mts
create mode 100644 packages/cli-helpers/tsconfig.build.cjs.json
diff --git a/packages/auth-providers/auth0/setup/.babelrc.js b/packages/auth-providers/auth0/setup/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/auth0/setup/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/auth0/setup/build.mts b/packages/auth-providers/auth0/setup/build.mts
new file mode 100644
index 000000000000..b35ee44f5def
--- /dev/null
+++ b/packages/auth-providers/auth0/setup/build.mts
@@ -0,0 +1,8 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['templates/**/*.template'],
+})
diff --git a/packages/auth-providers/auth0/setup/package.json b/packages/auth-providers/auth0/setup/package.json
index 55e82b1c7ab5..39e405be8728 100644
--- a/packages/auth-providers/auth0/setup/package.json
+++ b/packages/auth-providers/auth0/setup/package.json
@@ -7,30 +7,47 @@
"directory": "packages/auth-providers/auth0/setup"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./dist/setup": {
+ "types": "./dist/setup.d.ts",
+ "default": "./dist/setup.js"
+ },
+ "./dist/setupHandler": {
+ "types": "./dist/setupHandler.d.ts",
+ "default": "./dist/setupHandler.js"
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-auth0-setup.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@redwoodjs/cli-helpers": "workspace:*",
- "core-js": "3.38.0"
+ "@redwoodjs/cli-helpers": "workspace:*"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/yargs": "17.0.33",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/auth0/setup/tsconfig.json b/packages/auth-providers/auth0/setup/tsconfig.json
index 31fd2566f42e..7a7ec0d32fa2 100644
--- a/packages/auth-providers/auth0/setup/tsconfig.json
+++ b/packages/auth-providers/auth0/setup/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../cli-helpers" }]
diff --git a/packages/auth-providers/azureActiveDirectory/setup/.babelrc.js b/packages/auth-providers/azureActiveDirectory/setup/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/azureActiveDirectory/setup/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/azureActiveDirectory/setup/build.mts b/packages/auth-providers/azureActiveDirectory/setup/build.mts
new file mode 100644
index 000000000000..b35ee44f5def
--- /dev/null
+++ b/packages/auth-providers/azureActiveDirectory/setup/build.mts
@@ -0,0 +1,8 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['templates/**/*.template'],
+})
diff --git a/packages/auth-providers/azureActiveDirectory/setup/package.json b/packages/auth-providers/azureActiveDirectory/setup/package.json
index 1025e75c504e..0bc7183d7dc2 100644
--- a/packages/auth-providers/azureActiveDirectory/setup/package.json
+++ b/packages/auth-providers/azureActiveDirectory/setup/package.json
@@ -7,30 +7,47 @@
"directory": "packages/auth-providers/azureActiveDirectory/setup"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./dist/setup": {
+ "types": "./dist/setup.d.ts",
+ "default": "./dist/setup.js"
+ },
+ "./dist/setupHandler": {
+ "types": "./dist/setupHandler.d.ts",
+ "default": "./dist/setupHandler.js"
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-azure-active-directory-setup.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@redwoodjs/cli-helpers": "workspace:*",
- "core-js": "3.38.0"
+ "@redwoodjs/cli-helpers": "workspace:*"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/yargs": "17.0.33",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/azureActiveDirectory/setup/tsconfig.json b/packages/auth-providers/azureActiveDirectory/setup/tsconfig.json
index 31fd2566f42e..7a7ec0d32fa2 100644
--- a/packages/auth-providers/azureActiveDirectory/setup/tsconfig.json
+++ b/packages/auth-providers/azureActiveDirectory/setup/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../cli-helpers" }]
diff --git a/packages/auth-providers/clerk/setup/.babelrc.js b/packages/auth-providers/clerk/setup/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/clerk/setup/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/clerk/setup/build.mts b/packages/auth-providers/clerk/setup/build.mts
new file mode 100644
index 000000000000..b35ee44f5def
--- /dev/null
+++ b/packages/auth-providers/clerk/setup/build.mts
@@ -0,0 +1,8 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['templates/**/*.template'],
+})
diff --git a/packages/auth-providers/clerk/setup/package.json b/packages/auth-providers/clerk/setup/package.json
index 4db7c443a806..d568a43f86db 100644
--- a/packages/auth-providers/clerk/setup/package.json
+++ b/packages/auth-providers/clerk/setup/package.json
@@ -7,28 +7,45 @@
"directory": "packages/auth-providers/clerk/setup"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./dist/setup": {
+ "types": "./dist/setup.d.ts",
+ "default": "./dist/setup.js"
+ },
+ "./dist/setupHandler": {
+ "types": "./dist/setupHandler.d.ts",
+ "default": "./dist/setupHandler.js"
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-clerk-setup.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@redwoodjs/cli-helpers": "workspace:*",
- "core-js": "3.38.0"
+ "@redwoodjs/cli-helpers": "workspace:*"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/yargs": "17.0.33",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4"
},
"gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1"
diff --git a/packages/auth-providers/clerk/setup/tsconfig.json b/packages/auth-providers/clerk/setup/tsconfig.json
index 31fd2566f42e..7a7ec0d32fa2 100644
--- a/packages/auth-providers/clerk/setup/tsconfig.json
+++ b/packages/auth-providers/clerk/setup/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../cli-helpers" }]
diff --git a/packages/auth-providers/custom/setup/.babelrc.js b/packages/auth-providers/custom/setup/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/custom/setup/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/custom/setup/build.mts b/packages/auth-providers/custom/setup/build.mts
new file mode 100644
index 000000000000..b35ee44f5def
--- /dev/null
+++ b/packages/auth-providers/custom/setup/build.mts
@@ -0,0 +1,8 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['templates/**/*.template'],
+})
diff --git a/packages/auth-providers/custom/setup/package.json b/packages/auth-providers/custom/setup/package.json
index c0c8f397cb37..cb4cfdce6a65 100644
--- a/packages/auth-providers/custom/setup/package.json
+++ b/packages/auth-providers/custom/setup/package.json
@@ -7,30 +7,47 @@
"directory": "packages/auth-providers/custom/setup"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./dist/setup": {
+ "types": "./dist/setup.d.ts",
+ "default": "./dist/setup.js"
+ },
+ "./dist/setupHandler": {
+ "types": "./dist/setupHandler.d.ts",
+ "default": "./dist/setupHandler.js"
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-custom-setup.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@redwoodjs/cli-helpers": "workspace:*",
- "core-js": "3.38.0"
+ "@redwoodjs/cli-helpers": "workspace:*"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/yargs": "17.0.33",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/custom/setup/tsconfig.json b/packages/auth-providers/custom/setup/tsconfig.json
index 31fd2566f42e..7a7ec0d32fa2 100644
--- a/packages/auth-providers/custom/setup/tsconfig.json
+++ b/packages/auth-providers/custom/setup/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../cli-helpers" }]
diff --git a/packages/auth-providers/firebase/setup/.babelrc.js b/packages/auth-providers/firebase/setup/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/firebase/setup/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/firebase/setup/build.mts b/packages/auth-providers/firebase/setup/build.mts
new file mode 100644
index 000000000000..b35ee44f5def
--- /dev/null
+++ b/packages/auth-providers/firebase/setup/build.mts
@@ -0,0 +1,8 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['templates/**/*.template'],
+})
diff --git a/packages/auth-providers/firebase/setup/package.json b/packages/auth-providers/firebase/setup/package.json
index 3ffce06c2f4e..99e6d6117628 100644
--- a/packages/auth-providers/firebase/setup/package.json
+++ b/packages/auth-providers/firebase/setup/package.json
@@ -7,30 +7,47 @@
"directory": "packages/auth-providers/firebase/setup"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./dist/setup": {
+ "types": "./dist/setup.d.ts",
+ "default": "./dist/setup.js"
+ },
+ "./dist/setupHandler": {
+ "types": "./dist/setupHandler.d.ts",
+ "default": "./dist/setupHandler.js"
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-firebase-setup.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@redwoodjs/cli-helpers": "workspace:*",
- "core-js": "3.38.0"
+ "@redwoodjs/cli-helpers": "workspace:*"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/yargs": "17.0.33",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/firebase/setup/tsconfig.json b/packages/auth-providers/firebase/setup/tsconfig.json
index 31fd2566f42e..7a7ec0d32fa2 100644
--- a/packages/auth-providers/firebase/setup/tsconfig.json
+++ b/packages/auth-providers/firebase/setup/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../cli-helpers" }]
diff --git a/packages/auth-providers/netlify/setup/.babelrc.js b/packages/auth-providers/netlify/setup/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/netlify/setup/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/netlify/setup/build.mts b/packages/auth-providers/netlify/setup/build.mts
new file mode 100644
index 000000000000..b35ee44f5def
--- /dev/null
+++ b/packages/auth-providers/netlify/setup/build.mts
@@ -0,0 +1,8 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['templates/**/*.template'],
+})
diff --git a/packages/auth-providers/netlify/setup/package.json b/packages/auth-providers/netlify/setup/package.json
index 0f7dffb27062..3e1b27366483 100644
--- a/packages/auth-providers/netlify/setup/package.json
+++ b/packages/auth-providers/netlify/setup/package.json
@@ -7,30 +7,47 @@
"directory": "packages/auth-providers/netlify/setup"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./dist/setup": {
+ "types": "./dist/setup.d.ts",
+ "default": "./dist/setup.js"
+ },
+ "./dist/setupHandler": {
+ "types": "./dist/setupHandler.d.ts",
+ "default": "./dist/setupHandler.js"
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-netlify-setup.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@redwoodjs/cli-helpers": "workspace:*",
- "core-js": "3.38.0"
+ "@redwoodjs/cli-helpers": "workspace:*"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/yargs": "17.0.33",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/netlify/setup/tsconfig.json b/packages/auth-providers/netlify/setup/tsconfig.json
index 31fd2566f42e..7a7ec0d32fa2 100644
--- a/packages/auth-providers/netlify/setup/tsconfig.json
+++ b/packages/auth-providers/netlify/setup/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../cli-helpers" }]
diff --git a/packages/auth-providers/supabase/setup/.babelrc.js b/packages/auth-providers/supabase/setup/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/supabase/setup/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/supabase/setup/build.mts b/packages/auth-providers/supabase/setup/build.mts
new file mode 100644
index 000000000000..b35ee44f5def
--- /dev/null
+++ b/packages/auth-providers/supabase/setup/build.mts
@@ -0,0 +1,8 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['templates/**/*.template'],
+})
diff --git a/packages/auth-providers/supabase/setup/package.json b/packages/auth-providers/supabase/setup/package.json
index 8c3334654b3f..8374370226fb 100644
--- a/packages/auth-providers/supabase/setup/package.json
+++ b/packages/auth-providers/supabase/setup/package.json
@@ -7,28 +7,45 @@
"directory": "packages/auth-providers/supabase/setup"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./dist/setup": {
+ "types": "./dist/setup.d.ts",
+ "default": "./dist/setup.js"
+ },
+ "./dist/setupHandler": {
+ "types": "./dist/setupHandler.d.ts",
+ "default": "./dist/setupHandler.js"
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-supabase-setup.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@redwoodjs/cli-helpers": "workspace:*",
- "core-js": "3.38.0"
+ "@redwoodjs/cli-helpers": "workspace:*"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/yargs": "17.0.33",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4"
},
"gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1"
diff --git a/packages/auth-providers/supabase/setup/tsconfig.json b/packages/auth-providers/supabase/setup/tsconfig.json
index 31fd2566f42e..7a7ec0d32fa2 100644
--- a/packages/auth-providers/supabase/setup/tsconfig.json
+++ b/packages/auth-providers/supabase/setup/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../cli-helpers" }]
diff --git a/packages/auth-providers/supertokens/setup/.babelrc.js b/packages/auth-providers/supertokens/setup/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/supertokens/setup/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/supertokens/setup/build.mts b/packages/auth-providers/supertokens/setup/build.mts
new file mode 100644
index 000000000000..b35ee44f5def
--- /dev/null
+++ b/packages/auth-providers/supertokens/setup/build.mts
@@ -0,0 +1,8 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['templates/**/*.template'],
+})
diff --git a/packages/auth-providers/supertokens/setup/package.json b/packages/auth-providers/supertokens/setup/package.json
index b327fe20cb9c..c79730e601ea 100644
--- a/packages/auth-providers/supertokens/setup/package.json
+++ b/packages/auth-providers/supertokens/setup/package.json
@@ -7,31 +7,48 @@
"directory": "packages/auth-providers/supertokens/setup"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./dist/setup": {
+ "types": "./dist/setup.d.ts",
+ "default": "./dist/setup.js"
+ },
+ "./dist/setupHandler": {
+ "types": "./dist/setupHandler.d.ts",
+ "default": "./dist/setupHandler.js"
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-supertokens-setup.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@redwoodjs/cli-helpers": "workspace:*",
- "core-js": "3.38.0"
+ "@redwoodjs/cli-helpers": "workspace:*"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/yargs": "17.0.33",
+ "concurrently": "8.2.2",
"memfs": "4.11.1",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/supertokens/setup/tsconfig.json b/packages/auth-providers/supertokens/setup/tsconfig.json
index 31fd2566f42e..7a7ec0d32fa2 100644
--- a/packages/auth-providers/supertokens/setup/tsconfig.json
+++ b/packages/auth-providers/supertokens/setup/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../cli-helpers" }]
diff --git a/packages/cli-helpers/package.json b/packages/cli-helpers/package.json
index dde157b48432..974fbf958b4d 100644
--- a/packages/cli-helpers/package.json
+++ b/packages/cli-helpers/package.json
@@ -10,19 +10,34 @@
"type": "module",
"exports": {
".": {
- "types": "./dist/index.d.ts",
- "import": "./dist/index.js",
- "default": "./dist/cjs/index.js"
+ "import": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "default": {
+ "types": "./dist/cjs/index.d.ts",
+ "default": "./dist/cjs/index.js"
+ }
},
"./loadEnvFiles": {
- "types": "./dist/lib/loadEnvFiles.d.ts",
- "import": "./dist/lib/loadEnvFiles.js",
- "default": "./dist/cjs/lib/loadEnvFiles.js"
+ "import": {
+ "types": "./dist/lib/loadEnvFiles.d.ts",
+ "default": "./dist/lib/loadEnvFiles.js"
+ },
+ "default": {
+ "types": "./dist/cjs/lib/loadEnvFiles.d.ts",
+ "default": "./dist/cjs/lib/loadEnvFiles.js"
+ }
},
"./dist/lib/loadEnvFiles.js": {
- "types": "./dist/lib/loadEnvFiles.d.ts",
- "import": "./dist/lib/loadEnvFiles.js",
- "default": "./dist/cjs/lib/loadEnvFiles.js"
+ "import": {
+ "types": "./dist/lib/loadEnvFiles.d.ts",
+ "default": "./dist/lib/loadEnvFiles.js"
+ },
+ "default": {
+ "types": "./dist/cjs/lib/loadEnvFiles.d.ts",
+ "default": "./dist/cjs/lib/loadEnvFiles.js"
+ }
}
},
"types": "./dist/index.d.ts",
@@ -32,7 +47,7 @@
"scripts": {
"build": "tsx ./build.ts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-cli-helpers.tgz",
- "build:types": "tsc --build --verbose tsconfig.build.json",
+ "build:types": "tsc --build --verbose ./tsconfig.build.json ./tsconfig.build.cjs.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx\" --ignore dist --exec \"yarn build\"",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
diff --git a/packages/cli-helpers/tsconfig.build.cjs.json b/packages/cli-helpers/tsconfig.build.cjs.json
new file mode 100644
index 000000000000..40b7e3f22c42
--- /dev/null
+++ b/packages/cli-helpers/tsconfig.build.cjs.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.build.json",
+ "compilerOptions": {
+ "outDir": "dist/cjs",
+ "module": "CommonJS",
+ "moduleResolution": "Node10",
+ "tsBuildInfoFile": "./tsconfig.cjs.tsbuildinfo"
+ }
+}
diff --git a/packages/framework-tools/src/buildDefaults.ts b/packages/framework-tools/src/buildDefaults.ts
index 17de5d4fb8da..a908448e8307 100644
--- a/packages/framework-tools/src/buildDefaults.ts
+++ b/packages/framework-tools/src/buildDefaults.ts
@@ -1,4 +1,5 @@
import path from 'node:path'
+import { fileURLToPath } from 'node:url'
import * as esbuild from 'esbuild'
import type { BuildOptions as ESBuildOptions } from 'esbuild'
@@ -83,3 +84,46 @@ export async function build({
)
}
}
+
+interface CopyAssetsOptions {
+ buildFileUrl: string
+ patterns: string[]
+ ignore?: string[]
+}
+
+export async function copyAssets({
+ buildFileUrl,
+ patterns,
+ ignore,
+}: CopyAssetsOptions) {
+ const rootDirPath = path.dirname(fileURLToPath(buildFileUrl))
+ const srcDirPath = path.join(rootDirPath, 'src')
+ const distDirPath = path.join(rootDirPath, 'dist')
+
+ let pathnames = await fg(patterns, {
+ absolute: true,
+ cwd: srcDirPath,
+ ignore: ignore ?? defaultIgnorePatterns,
+ })
+
+ // For Windows.
+ pathnames = pathnames.map((p) => path.normalize(p))
+
+ for (const pathname of pathnames) {
+ const distPathname = pathname.replace(srcDirPath, distDirPath)
+
+ try {
+ await fs.mkdirp(path.dirname(distPathname))
+ await fs.copyFile(pathname, distPathname)
+ console.log(
+ `Copied asset into dist: ${path.relative(distDirPath, distPathname)}`,
+ )
+ } catch (error) {
+ console.error(
+ `Couldn't copy ${pathname} to ${distPathname}. ` +
+ `(Replaced ${srcDirPath} with ${distDirPath} to get the dist pathname.)`,
+ )
+ throw error
+ }
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index 32dec80ebcd6..149a70b4eed0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7275,12 +7275,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-auth0-setup@workspace:packages/auth-providers/auth0/setup"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/cli-helpers": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/yargs": "npm:17.0.33"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7327,12 +7328,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-azure-active-directory-setup@workspace:packages/auth-providers/azureActiveDirectory/setup"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/cli-helpers": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/yargs": "npm:17.0.33"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7378,12 +7380,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-clerk-setup@workspace:packages/auth-providers/clerk/setup"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/cli-helpers": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/yargs": "npm:17.0.33"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
languageName: unknown
linkType: soft
@@ -7412,12 +7415,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-custom-setup@workspace:packages/auth-providers/custom/setup"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/cli-helpers": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/yargs": "npm:17.0.33"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7524,12 +7528,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-firebase-setup@workspace:packages/auth-providers/firebase/setup"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/cli-helpers": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/yargs": "npm:17.0.33"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7576,12 +7581,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-netlify-setup@workspace:packages/auth-providers/netlify/setup"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/cli-helpers": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/yargs": "npm:17.0.33"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7650,12 +7656,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-supabase-setup@workspace:packages/auth-providers/supabase/setup"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/cli-helpers": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/yargs": "npm:17.0.33"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
languageName: unknown
linkType: soft
@@ -7703,13 +7710,14 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-supertokens-setup@workspace:packages/auth-providers/supertokens/setup"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/cli-helpers": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/yargs": "npm:17.0.33"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
memfs: "npm:4.11.1"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
From ddfb0805708adc1bc024bb23106f59138a376723 Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sun, 18 Aug 2024 02:18:26 +0100
Subject: [PATCH 44/52] fix(auth-providers): Move away from babel for building
'api' packages (#11301)
This switches from babel to esbuild for building the 'api' auth provider
packages.
I added `exports` to the package.json. I added all existing files to the
exports field.
I updated the tsconfig files to use `Node16` module and module
resolution. I explicitly set the tsbuildinfo file too. My thinking is
that we can be explicit to start with. Once we have all our packages
over to esbuild and know the lay of the land then we can
deduplicate/refactor our configs.
This is a chore but does effect what gets shipped so I'll label it as
fix and mark it as next-release.
---
packages/auth-providers/auth0/api/.babelrc.js | 1 -
packages/auth-providers/auth0/api/build.mts | 3 +
.../auth-providers/auth0/api/package.json | 31 ++++++--
.../auth-providers/auth0/api/tsconfig.json | 5 +-
.../azureActiveDirectory/api/.babelrc.js | 1 -
.../azureActiveDirectory/api/build.mts | 3 +
.../azureActiveDirectory/api/package.json | 31 ++++++--
.../azureActiveDirectory/api/tsconfig.json | 5 +-
packages/auth-providers/clerk/api/.babelrc.js | 1 -
packages/auth-providers/clerk/api/build.mts | 3 +
.../auth-providers/clerk/api/package.json | 33 ++++++---
.../auth-providers/clerk/api/tsconfig.json | 5 +-
.../auth-providers/dbAuth/api/.babelrc.js | 1 -
packages/auth-providers/dbAuth/api/build.mts | 3 +
.../auth-providers/dbAuth/api/package.json | 49 +++++++++++--
.../auth-providers/dbAuth/api/tsconfig.json | 5 +-
.../auth-providers/firebase/api/.babelrc.js | 1 -
.../auth-providers/firebase/api/build.mts | 3 +
.../auth-providers/firebase/api/package.json | 31 ++++++--
.../auth-providers/firebase/api/tsconfig.json | 5 +-
.../auth-providers/netlify/api/.babelrc.js | 1 -
packages/auth-providers/netlify/api/build.mts | 3 +
.../auth-providers/netlify/api/package.json | 31 ++++++--
.../auth-providers/netlify/api/tsconfig.json | 5 +-
.../auth-providers/supabase/api/.babelrc.js | 1 -
.../auth-providers/supabase/api/build.mts | 3 +
.../auth-providers/supabase/api/package.json | 31 ++++++--
.../auth-providers/supabase/api/tsconfig.json | 5 +-
.../supertokens/api/.babelrc.js | 1 -
.../auth-providers/supertokens/api/build.mts | 3 +
.../supertokens/api/package.json | 31 ++++++--
.../supertokens/api/tsconfig.json | 5 +-
yarn.lock | 72 ++++++++++---------
33 files changed, 307 insertions(+), 105 deletions(-)
delete mode 100644 packages/auth-providers/auth0/api/.babelrc.js
create mode 100644 packages/auth-providers/auth0/api/build.mts
delete mode 100644 packages/auth-providers/azureActiveDirectory/api/.babelrc.js
create mode 100644 packages/auth-providers/azureActiveDirectory/api/build.mts
delete mode 100644 packages/auth-providers/clerk/api/.babelrc.js
create mode 100644 packages/auth-providers/clerk/api/build.mts
delete mode 100644 packages/auth-providers/dbAuth/api/.babelrc.js
create mode 100644 packages/auth-providers/dbAuth/api/build.mts
delete mode 100644 packages/auth-providers/firebase/api/.babelrc.js
create mode 100644 packages/auth-providers/firebase/api/build.mts
delete mode 100644 packages/auth-providers/netlify/api/.babelrc.js
create mode 100644 packages/auth-providers/netlify/api/build.mts
delete mode 100644 packages/auth-providers/supabase/api/.babelrc.js
create mode 100644 packages/auth-providers/supabase/api/build.mts
delete mode 100644 packages/auth-providers/supertokens/api/.babelrc.js
create mode 100644 packages/auth-providers/supertokens/api/build.mts
diff --git a/packages/auth-providers/auth0/api/.babelrc.js b/packages/auth-providers/auth0/api/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/auth0/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/auth0/api/build.mts b/packages/auth-providers/auth0/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/auth-providers/auth0/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/auth-providers/auth0/api/package.json b/packages/auth-providers/auth0/api/package.json
index 5d6f1bbb8ede..10cbd6251643 100644
--- a/packages/auth-providers/auth0/api/package.json
+++ b/packages/auth-providers/auth0/api/package.json
@@ -7,32 +7,49 @@
"directory": "packages/auth-providers/auth0/api"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "default": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "./dist/decoder": {
+ "default": {
+ "types": "./dist/decoder.d.ts",
+ "default": "./dist/decoder.js"
+ }
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-auth0-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "core-js": "3.38.0",
"jsonwebtoken": "9.0.2",
"jwks-rsa": "3.1.0"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
"@redwoodjs/api": "workspace:*",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/jsonwebtoken": "9.0.6",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/auth0/api/tsconfig.json b/packages/auth-providers/auth0/api/tsconfig.json
index 6b8ec4efb25c..5f48476e8ec5 100644
--- a/packages/auth-providers/auth0/api/tsconfig.json
+++ b/packages/auth-providers/auth0/api/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../api" }]
diff --git a/packages/auth-providers/azureActiveDirectory/api/.babelrc.js b/packages/auth-providers/azureActiveDirectory/api/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/azureActiveDirectory/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/azureActiveDirectory/api/build.mts b/packages/auth-providers/azureActiveDirectory/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/auth-providers/azureActiveDirectory/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/auth-providers/azureActiveDirectory/api/package.json b/packages/auth-providers/azureActiveDirectory/api/package.json
index 9f236bf572e4..76302388d6fe 100644
--- a/packages/auth-providers/azureActiveDirectory/api/package.json
+++ b/packages/auth-providers/azureActiveDirectory/api/package.json
@@ -7,33 +7,50 @@
"directory": "packages/auth-providers/azureActiveDirectory/api"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "default": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "./dist/decoder": {
+ "default": {
+ "types": "./dist/decoder.d.ts",
+ "default": "./dist/decoder.js"
+ }
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-azure-active-directory-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "core-js": "3.38.0",
"jsonwebtoken": "9.0.2",
"jwks-rsa": "3.1.0"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
"@redwoodjs/api": "workspace:*",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/aws-lambda": "8.10.143",
"@types/jsonwebtoken": "9.0.6",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/azureActiveDirectory/api/tsconfig.json b/packages/auth-providers/azureActiveDirectory/api/tsconfig.json
index 6b8ec4efb25c..5f48476e8ec5 100644
--- a/packages/auth-providers/azureActiveDirectory/api/tsconfig.json
+++ b/packages/auth-providers/azureActiveDirectory/api/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../api" }]
diff --git a/packages/auth-providers/clerk/api/.babelrc.js b/packages/auth-providers/clerk/api/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/clerk/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/clerk/api/build.mts b/packages/auth-providers/clerk/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/auth-providers/clerk/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/auth-providers/clerk/api/package.json b/packages/auth-providers/clerk/api/package.json
index e77bbf75e210..200b743d2245 100644
--- a/packages/auth-providers/clerk/api/package.json
+++ b/packages/auth-providers/clerk/api/package.json
@@ -7,31 +7,48 @@
"directory": "packages/auth-providers/clerk/api"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "default": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "./dist/decoder": {
+ "default": {
+ "types": "./dist/decoder.d.ts",
+ "default": "./dist/decoder.js"
+ }
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-clerk-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "@clerk/clerk-sdk-node": "4.13.21",
- "core-js": "3.38.0"
+ "@clerk/clerk-sdk-node": "4.13.21"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
"@redwoodjs/api": "workspace:*",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/aws-lambda": "8.10.143",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/clerk/api/tsconfig.json b/packages/auth-providers/clerk/api/tsconfig.json
index 6b8ec4efb25c..5f48476e8ec5 100644
--- a/packages/auth-providers/clerk/api/tsconfig.json
+++ b/packages/auth-providers/clerk/api/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../api" }]
diff --git a/packages/auth-providers/dbAuth/api/.babelrc.js b/packages/auth-providers/dbAuth/api/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/dbAuth/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/dbAuth/api/build.mts b/packages/auth-providers/dbAuth/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/auth-providers/dbAuth/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/auth-providers/dbAuth/api/package.json b/packages/auth-providers/dbAuth/api/package.json
index 9b221d731c91..fceceea517b3 100644
--- a/packages/auth-providers/dbAuth/api/package.json
+++ b/packages/auth-providers/dbAuth/api/package.json
@@ -7,36 +7,71 @@
"directory": "packages/auth-providers/dbAuth/api"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "default": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "./dist/decoder": {
+ "default": {
+ "types": "./dist/decoder.d.ts",
+ "default": "./dist/decoder.js"
+ }
+ },
+ "./dist/errors": {
+ "default": {
+ "types": "./dist/errors.d.ts",
+ "default": "./dist/errors.js"
+ }
+ },
+ "./dist/shared": {
+ "default": {
+ "types": "./dist/shared.d.ts",
+ "default": "./dist/shared.js"
+ }
+ },
+ "./dist/DbAuthHandler": {
+ "default": {
+ "types": "./dist/DbAuthHandler.d.ts",
+ "default": "./dist/DbAuthHandler.js"
+ }
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-dbauth-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
"@redwoodjs/project-config": "workspace:*",
"base64url": "3.0.1",
- "core-js": "3.38.0",
"md5": "2.3.0",
"uuid": "10.0.0"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
"@redwoodjs/api": "workspace:*",
+ "@redwoodjs/framework-tools": "workspace:*",
"@simplewebauthn/server": "7.4.0",
"@types/md5": "2.3.5",
"@types/uuid": "10.0.0",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/dbAuth/api/tsconfig.json b/packages/auth-providers/dbAuth/api/tsconfig.json
index 9cf53b22c9c0..f01d25beaf87 100644
--- a/packages/auth-providers/dbAuth/api/tsconfig.json
+++ b/packages/auth-providers/dbAuth/api/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [
diff --git a/packages/auth-providers/firebase/api/.babelrc.js b/packages/auth-providers/firebase/api/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/firebase/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/firebase/api/build.mts b/packages/auth-providers/firebase/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/auth-providers/firebase/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/auth-providers/firebase/api/package.json b/packages/auth-providers/firebase/api/package.json
index e904c9bc9e89..f0247dbb837e 100644
--- a/packages/auth-providers/firebase/api/package.json
+++ b/packages/auth-providers/firebase/api/package.json
@@ -7,31 +7,48 @@
"directory": "packages/auth-providers/firebase/api"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "default": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "./dist/decoder": {
+ "default": {
+ "types": "./dist/decoder.d.ts",
+ "default": "./dist/decoder.js"
+ }
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-firebase-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "core-js": "3.38.0",
"firebase-admin": "12.3.1"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
"@redwoodjs/api": "workspace:*",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/aws-lambda": "8.10.143",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/firebase/api/tsconfig.json b/packages/auth-providers/firebase/api/tsconfig.json
index 6b8ec4efb25c..5f48476e8ec5 100644
--- a/packages/auth-providers/firebase/api/tsconfig.json
+++ b/packages/auth-providers/firebase/api/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../api" }]
diff --git a/packages/auth-providers/netlify/api/.babelrc.js b/packages/auth-providers/netlify/api/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/netlify/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/netlify/api/build.mts b/packages/auth-providers/netlify/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/auth-providers/netlify/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/auth-providers/netlify/api/package.json b/packages/auth-providers/netlify/api/package.json
index 693903bf910d..9d56dbb8e14b 100644
--- a/packages/auth-providers/netlify/api/package.json
+++ b/packages/auth-providers/netlify/api/package.json
@@ -7,32 +7,49 @@
"directory": "packages/auth-providers/netlify/api"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "default": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "./dist/decoder": {
+ "default": {
+ "types": "./dist/decoder.d.ts",
+ "default": "./dist/decoder.js"
+ }
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-netlify-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "core-js": "3.38.0",
"jsonwebtoken": "9.0.2"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
"@redwoodjs/api": "workspace:*",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/aws-lambda": "8.10.143",
"@types/jsonwebtoken": "9.0.6",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/netlify/api/tsconfig.json b/packages/auth-providers/netlify/api/tsconfig.json
index 6b8ec4efb25c..5f48476e8ec5 100644
--- a/packages/auth-providers/netlify/api/tsconfig.json
+++ b/packages/auth-providers/netlify/api/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../api" }]
diff --git a/packages/auth-providers/supabase/api/.babelrc.js b/packages/auth-providers/supabase/api/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/supabase/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/supabase/api/build.mts b/packages/auth-providers/supabase/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/auth-providers/supabase/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/auth-providers/supabase/api/package.json b/packages/auth-providers/supabase/api/package.json
index ba5b8a375800..309e0d0e9b2f 100644
--- a/packages/auth-providers/supabase/api/package.json
+++ b/packages/auth-providers/supabase/api/package.json
@@ -7,33 +7,50 @@
"directory": "packages/auth-providers/supabase/api"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "default": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "./dist/decoder": {
+ "default": {
+ "types": "./dist/decoder.d.ts",
+ "default": "./dist/decoder.js"
+ }
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-supabase-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
"@redwoodjs/api": "workspace:*",
"@supabase/ssr": "0.4.0",
- "core-js": "3.38.0",
"jsonwebtoken": "9.0.2"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/aws-lambda": "8.10.143",
"@types/jsonwebtoken": "9.0.6",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/supabase/api/tsconfig.json b/packages/auth-providers/supabase/api/tsconfig.json
index 6b8ec4efb25c..5f48476e8ec5 100644
--- a/packages/auth-providers/supabase/api/tsconfig.json
+++ b/packages/auth-providers/supabase/api/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../api" }]
diff --git a/packages/auth-providers/supertokens/api/.babelrc.js b/packages/auth-providers/supertokens/api/.babelrc.js
deleted file mode 100644
index 4312886a07e5..000000000000
--- a/packages/auth-providers/supertokens/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../../../babel.config.js' }
diff --git a/packages/auth-providers/supertokens/api/build.mts b/packages/auth-providers/supertokens/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/auth-providers/supertokens/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/auth-providers/supertokens/api/package.json b/packages/auth-providers/supertokens/api/package.json
index dced3a306d58..95329899edad 100644
--- a/packages/auth-providers/supertokens/api/package.json
+++ b/packages/auth-providers/supertokens/api/package.json
@@ -7,32 +7,49 @@
"directory": "packages/auth-providers/supertokens/api"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "default": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "./dist/decoder": {
+ "default": {
+ "types": "./dist/decoder.d.ts",
+ "default": "./dist/decoder.js"
+ }
+ }
+ },
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-auth-supertokens-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx,template\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
- "core-js": "3.38.0",
"jsonwebtoken": "9.0.2",
"jwks-rsa": "3.1.0"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
"@redwoodjs/api": "workspace:*",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/jsonwebtoken": "9.0.6",
+ "concurrently": "8.2.2",
+ "publint": "0.2.10",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/auth-providers/supertokens/api/tsconfig.json b/packages/auth-providers/supertokens/api/tsconfig.json
index 6b8ec4efb25c..5f48476e8ec5 100644
--- a/packages/auth-providers/supertokens/api/tsconfig.json
+++ b/packages/auth-providers/supertokens/api/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"strict": true,
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src"],
"references": [{ "path": "../../../api" }]
diff --git a/yarn.lock b/yarn.lock
index 149a70b4eed0..2c7089a52f68 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7258,14 +7258,15 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-auth0-api@workspace:packages/auth-providers/auth0/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/api": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/jsonwebtoken": "npm:9.0.6"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
jsonwebtoken: "npm:9.0.2"
jwks-rsa: "npm:3.1.0"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7310,15 +7311,16 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-azure-active-directory-api@workspace:packages/auth-providers/azureActiveDirectory/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/api": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/aws-lambda": "npm:8.10.143"
"@types/jsonwebtoken": "npm:9.0.6"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
jsonwebtoken: "npm:9.0.2"
jwks-rsa: "npm:3.1.0"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7364,13 +7366,14 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-clerk-api@workspace:packages/auth-providers/clerk/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@clerk/clerk-sdk-node": "npm:4.13.21"
"@redwoodjs/api": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/aws-lambda": "npm:8.10.143"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7431,17 +7434,18 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-dbauth-api@workspace:packages/auth-providers/dbAuth/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/api": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@redwoodjs/project-config": "workspace:*"
"@simplewebauthn/server": "npm:7.4.0"
"@types/md5": "npm:2.3.5"
"@types/uuid": "npm:10.0.0"
base64url: "npm:3.0.1"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
md5: "npm:2.3.0"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
uuid: "npm:10.0.0"
vitest: "npm:2.0.5"
@@ -7512,13 +7516,14 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-firebase-api@workspace:packages/auth-providers/firebase/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/api": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/aws-lambda": "npm:8.10.143"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
firebase-admin: "npm:12.3.1"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7564,14 +7569,15 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-netlify-api@workspace:packages/auth-providers/netlify/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/api": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/aws-lambda": "npm:8.10.143"
"@types/jsonwebtoken": "npm:9.0.6"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
jsonwebtoken: "npm:9.0.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7616,15 +7622,16 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-supabase-api@workspace:packages/auth-providers/supabase/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/api": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@supabase/ssr": "npm:0.4.0"
"@types/aws-lambda": "npm:8.10.143"
"@types/jsonwebtoken": "npm:9.0.6"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
jsonwebtoken: "npm:9.0.2"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
languageName: unknown
@@ -7691,14 +7698,15 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/auth-supertokens-api@workspace:packages/auth-providers/supertokens/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@redwoodjs/api": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/jsonwebtoken": "npm:9.0.6"
- core-js: "npm:3.38.0"
+ concurrently: "npm:8.2.2"
jsonwebtoken: "npm:9.0.2"
jwks-rsa: "npm:3.1.0"
+ publint: "npm:0.2.10"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
peerDependencies:
From ab6633abf20af81558d805b4acbc2b62fef885ff Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sun, 18 Aug 2024 03:40:27 +0100
Subject: [PATCH 45/52] fix(internal): Move away from babel for building
package (#11304)
This switches to esbuild over babel. I added exports entries for every
use I could find in the framework. Internal isn't designed to be used
externally so people should not have considered reaching into dist as
stable anyway.
I decided to mark this as breaking since it ultimately is.
---
packages/internal/.babelrc.js | 1 -
packages/internal/build.mts | 7 +++++
packages/internal/package.json | 56 ++++++++++++++++++++++++++++++---
packages/internal/tsconfig.json | 5 ++-
yarn.lock | 5 ++-
5 files changed, 67 insertions(+), 7 deletions(-)
delete mode 100644 packages/internal/.babelrc.js
create mode 100644 packages/internal/build.mts
diff --git a/packages/internal/.babelrc.js b/packages/internal/.babelrc.js
deleted file mode 100644
index 3b2c815712d9..000000000000
--- a/packages/internal/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../babel.config.js' }
diff --git a/packages/internal/build.mts b/packages/internal/build.mts
new file mode 100644
index 000000000000..ecc19f75a3c8
--- /dev/null
+++ b/packages/internal/build.mts
@@ -0,0 +1,7 @@
+import { build, copyAssets } from '@redwoodjs/framework-tools'
+
+await build()
+await copyAssets({
+ buildFileUrl: import.meta.url,
+ patterns: ['generate/templates/**/*.template'],
+})
diff --git a/packages/internal/package.json b/packages/internal/package.json
index 21f7015e27f1..eaedd313d24a 100644
--- a/packages/internal/package.json
+++ b/packages/internal/package.json
@@ -7,6 +7,50 @@
"directory": "packages/internal"
},
"license": "MIT",
+ "type": "commonjs",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./package.json": "./package.json",
+ "./dist/dev": {
+ "types": "./dist/dev.d.ts",
+ "default": "./dist/dev.js"
+ },
+ "./dist/files": {
+ "types": "./dist/files.d.ts",
+ "default": "./dist/files.js"
+ },
+ "./dist/files.js": {
+ "types": "./dist/files.d.ts",
+ "default": "./dist/files.js"
+ },
+ "./dist/gql": {
+ "types": "./dist/gql.d.ts",
+ "default": "./dist/gql.js"
+ },
+ "./dist/routes.js": {
+ "types": "./dist/routes.d.ts",
+ "default": "./dist/routes.js"
+ },
+ "./dist/ts2js": {
+ "types": "./dist/ts2js.d.ts",
+ "default": "./dist/ts2js.js"
+ },
+ "./dist/validateSchema": {
+ "types": "./dist/validateSchema.d.ts",
+ "default": "./dist/validateSchema.js"
+ },
+ "./dist/build/api": {
+ "types": "./dist/build/api.d.ts",
+ "default": "./dist/build/api.js"
+ },
+ "./dist/generate/generate": {
+ "types": "./dist/generate/generate.d.ts",
+ "default": "./dist/generate/generate.js"
+ }
+ },
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
@@ -17,12 +61,13 @@
"dist"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
+ "build": "tsx ./build.mts && yarn build:types",
"build:clean-dist": "rimraf 'dist/**/*/__tests__' --glob",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\" --copy-files --no-copy-ignored && yarn build:clean-dist",
"build:pack": "yarn pack -o redwoodjs-internal.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"fix:permissions": "chmod +x dist/generate/generate.js dist/generate/watch.js",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
@@ -70,9 +115,12 @@
"typescript": "5.5.4"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/fs-extra": "11.0.4",
+ "concurrently": "8.2.2",
"graphql-tag": "2.12.6",
+ "publint": "0.2.10",
"tsx": "4.17.0",
"vitest": "2.0.5"
},
diff --git a/packages/internal/tsconfig.json b/packages/internal/tsconfig.json
index cbc1a3ef398c..e0ba04a6a84f 100644
--- a/packages/internal/tsconfig.json
+++ b/packages/internal/tsconfig.json
@@ -2,7 +2,10 @@
"extends": "../../tsconfig.compilerOption.json",
"compilerOptions": {
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src/**/*", "./ambient.d.ts"],
"references": [
diff --git a/yarn.lock b/yarn.lock
index 2c7089a52f68..a085359a165d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8213,7 +8213,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/internal@workspace:packages/internal"
dependencies:
- "@babel/cli": "npm:7.24.8"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@babel/core": "npm:^7.22.20"
"@babel/parser": "npm:^7.22.16"
"@babel/plugin-transform-react-jsx": "npm:^7.22.15"
@@ -8233,12 +8233,14 @@ __metadata:
"@graphql-codegen/typescript-resolvers": "npm:3.2.1"
"@graphql-tools/documents": "npm:1.0.1"
"@redwoodjs/babel-config": "workspace:*"
+ "@redwoodjs/framework-tools": "workspace:*"
"@redwoodjs/graphql-server": "workspace:*"
"@redwoodjs/project-config": "workspace:*"
"@redwoodjs/router": "workspace:*"
"@sdl-codegen/node": "npm:1.0.2"
"@types/fs-extra": "npm:11.0.4"
chalk: "npm:4.1.2"
+ concurrently: "npm:8.2.2"
core-js: "npm:3.38.0"
deepmerge: "npm:4.3.1"
esbuild: "npm:0.23.0"
@@ -8248,6 +8250,7 @@ __metadata:
graphql-tag: "npm:2.12.6"
kill-port: "npm:1.6.1"
prettier: "npm:3.3.3"
+ publint: "npm:0.2.10"
rimraf: "npm:6.0.1"
source-map: "npm:0.7.4"
string-env-interpolation: "npm:1.0.1"
From c72c31780a84fa81b9e639ef77cfee680a4f442f Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sun, 18 Aug 2024 03:59:13 +0100
Subject: [PATCH 46/52] fix(api): Move away from babel for building package
(#11305)
Build the api package with esbuild instead of babel.
I will follow up with a separate specific PR to introduce the exports
field and remove the need for the files redirection thing that had to be
done previously.
---
packages/api/.babelrc.js | 1 -
packages/api/build.mts | 3 +++
packages/api/package.json | 17 ++++++++++-------
packages/api/tsconfig.json | 5 ++++-
yarn.lock | 9 +++++----
5 files changed, 22 insertions(+), 13 deletions(-)
delete mode 100644 packages/api/.babelrc.js
create mode 100644 packages/api/build.mts
diff --git a/packages/api/.babelrc.js b/packages/api/.babelrc.js
deleted file mode 100644
index 3b2c815712d9..000000000000
--- a/packages/api/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../babel.config.js' }
diff --git a/packages/api/build.mts b/packages/api/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/api/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/api/package.json b/packages/api/package.json
index f984a71c2958..1a6c91f9e9e2 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -7,6 +7,7 @@
"directory": "packages/api"
},
"license": "MIT",
+ "type": "commonjs",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
@@ -22,21 +23,20 @@
"webhooks"
],
"scripts": {
- "build": "yarn build:js && yarn build:types",
- "build:js": "babel src -d dist --extensions \".js,.jsx,.ts,.tsx\"",
+ "build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-api.tgz",
- "build:types": "tsc --build --verbose",
+ "build:types": "tsc --build --verbose ./tsconfig.json",
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx\" --ignore dist --exec \"yarn build\"",
+ "check:attw": "yarn attw -P",
+ "check:package": "concurrently npm:check:attw yarn:publint",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
- "@babel/runtime-corejs3": "7.25.0",
"@prisma/client": "5.18.0",
"@whatwg-node/fetch": "0.9.20",
"cookie": "0.6.0",
- "core-js": "3.38.0",
"humanize-string": "2.1.0",
"jsonwebtoken": "9.0.2",
"pascalcase": "1.0.0",
@@ -44,17 +44,20 @@
"title-case": "3.0.3"
},
"devDependencies": {
- "@babel/cli": "7.24.8",
- "@babel/core": "^7.22.20",
+ "@arethetypeswrong/cli": "0.15.4",
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/aws-lambda": "8.10.143",
"@types/jsonwebtoken": "9.0.6",
"@types/memjs": "1",
"@types/pascalcase": "1.0.3",
"@types/split2": "4.2.3",
+ "concurrently": "8.2.2",
"memjs": "1.3.2",
+ "publint": "0.2.10",
"redis": "4.7.0",
"split2": "4.2.0",
"ts-toolbelt": "9.6.0",
+ "tsx": "4.17.0",
"typescript": "5.5.4",
"vitest": "2.0.5"
},
diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json
index 808b868a6dca..801c836e11b7 100644
--- a/packages/api/tsconfig.json
+++ b/packages/api/tsconfig.json
@@ -2,7 +2,10 @@
"extends": "../../tsconfig.compilerOption.json",
"compilerOptions": {
"rootDir": "src",
- "outDir": "dist"
+ "outDir": "dist",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"include": ["src/**/*"]
}
diff --git a/yarn.lock b/yarn.lock
index a085359a165d..95119284b8d4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7215,27 +7215,28 @@ __metadata:
version: 0.0.0-use.local
resolution: "@redwoodjs/api@workspace:packages/api"
dependencies:
- "@babel/cli": "npm:7.24.8"
- "@babel/core": "npm:^7.22.20"
- "@babel/runtime-corejs3": "npm:7.25.0"
+ "@arethetypeswrong/cli": "npm:0.15.4"
"@prisma/client": "npm:5.18.0"
+ "@redwoodjs/framework-tools": "workspace:*"
"@types/aws-lambda": "npm:8.10.143"
"@types/jsonwebtoken": "npm:9.0.6"
"@types/memjs": "npm:1"
"@types/pascalcase": "npm:1.0.3"
"@types/split2": "npm:4.2.3"
"@whatwg-node/fetch": "npm:0.9.20"
+ concurrently: "npm:8.2.2"
cookie: "npm:0.6.0"
- core-js: "npm:3.38.0"
humanize-string: "npm:2.1.0"
jsonwebtoken: "npm:9.0.2"
memjs: "npm:1.3.2"
pascalcase: "npm:1.0.0"
pino: "npm:9.3.2"
+ publint: "npm:0.2.10"
redis: "npm:4.7.0"
split2: "npm:4.2.0"
title-case: "npm:3.0.3"
ts-toolbelt: "npm:9.6.0"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
peerDependencies:
From ee4fb18360751b78ce82aa5d8e130fe00798b7d3 Mon Sep 17 00:00:00 2001
From: Josh GM Walker <56300765+Josh-Walker-GM@users.noreply.github.com>
Date: Sun, 18 Aug 2024 04:23:27 +0100
Subject: [PATCH 47/52] fix(codemods): Move away from babel for building
package (#11306)
Switch from babel to esbuild for building the codemods package.
I consider this package a little more fragile than most of ours simply
because it contains code which was written a long time ago. I haven't
removed any dependencies or introduce the exports field here. Can follow
up with that when/if needed.
---
packages/codemods/.babelrc.js | 1 -
packages/codemods/build.mts | 3 +++
packages/codemods/package.json | 8 ++++++--
packages/codemods/tsconfig.json | 4 +++-
yarn.lock | 3 +++
5 files changed, 15 insertions(+), 4 deletions(-)
delete mode 100644 packages/codemods/.babelrc.js
create mode 100644 packages/codemods/build.mts
diff --git a/packages/codemods/.babelrc.js b/packages/codemods/.babelrc.js
deleted file mode 100644
index 3b2c815712d9..000000000000
--- a/packages/codemods/.babelrc.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: '../../babel.config.js' }
diff --git a/packages/codemods/build.mts b/packages/codemods/build.mts
new file mode 100644
index 000000000000..16175a6725c0
--- /dev/null
+++ b/packages/codemods/build.mts
@@ -0,0 +1,3 @@
+import { build } from '@redwoodjs/framework-tools'
+
+await build()
diff --git a/packages/codemods/package.json b/packages/codemods/package.json
index 33434cb0c239..8208a6138a67 100644
--- a/packages/codemods/package.json
+++ b/packages/codemods/package.json
@@ -8,15 +8,16 @@
"directory": "packages/codemods"
},
"license": "MIT",
+ "type": "commonjs",
"bin": "./dist/codemods.js",
"files": [
"dist"
],
"scripts": {
- "build": "yarn build:js",
- "build:js": "babel src -d dist --extensions \".js,.ts\" --ignore \"src/**/__tests__/**\" --ignore \"src/**/__testfixtures__/**\"",
+ "build": "tsx ./build.mts",
"build:pack": "yarn pack -o redwoodjs-codemods.tgz",
"build:watch": "nodemon --watch src --ignore dist --exec \"yarn build\"",
+ "check:package": "yarn publint",
"generate:codemod": "yarn node ./tasks/generateCodemod/generateCodemod.mjs",
"prepublishOnly": "yarn build",
"test": "vitest run",
@@ -48,12 +49,15 @@
"yargs": "17.7.2"
},
"devDependencies": {
+ "@redwoodjs/framework-tools": "workspace:*",
"@types/babel__core": "7.20.5",
"@types/fs-extra": "11.0.4",
"@types/jscodeshift": "0.11.11",
"@types/yargs": "17.0.33",
"fs-extra": "11.2.0",
+ "publint": "0.2.10",
"tempy": "1.0.1",
+ "tsx": "4.17.0",
"vitest": "2.0.5"
},
"gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1"
diff --git a/packages/codemods/tsconfig.json b/packages/codemods/tsconfig.json
index 43e8728515c0..fbb47936232b 100644
--- a/packages/codemods/tsconfig.json
+++ b/packages/codemods/tsconfig.json
@@ -3,7 +3,9 @@
"compilerOptions": {
"rootDir": "src",
"emitDeclarationOnly": false,
- "noEmit": true
+ "noEmit": true,
+ "module": "Node16",
+ "moduleResolution": "Node16"
},
"include": ["src", "./testUtils.d.ts"],
"exclude": ["**/__testfixtures__"]
diff --git a/yarn.lock b/yarn.lock
index 95119284b8d4..0c371ec171ef 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7967,6 +7967,7 @@ __metadata:
"@babel/plugin-transform-typescript": "npm:^7.22.15"
"@babel/runtime-corejs3": "npm:7.25.0"
"@babel/traverse": "npm:^7.22.20"
+ "@redwoodjs/framework-tools": "workspace:*"
"@redwoodjs/project-config": "workspace:*"
"@svgr/core": "npm:8.1.0"
"@svgr/plugin-jsx": "npm:8.1.0"
@@ -7986,8 +7987,10 @@ __metadata:
jscodeshift: "npm:17.0.0"
pascalcase: "npm:1.0.0"
prettier: "npm:3.3.3"
+ publint: "npm:0.2.10"
tasuku: "npm:2.0.1"
tempy: "npm:1.0.1"
+ tsx: "npm:4.17.0"
typescript: "npm:5.5.4"
vitest: "npm:2.0.5"
yargs: "npm:17.7.2"
From 7a0251684cd4291f8bab7e4609ab1ffd35038be5 Mon Sep 17 00:00:00 2001
From: Tobbe Lundberg
Date: Sun, 18 Aug 2024 18:19:32 +0200
Subject: [PATCH 48/52] chore(README): Move Kris from Maintainer to Alumni
(#11308)
---
README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 295271c7656d..082bc32428bd 100644
--- a/README.md
+++ b/README.md
@@ -170,7 +170,6 @@ _A gigantic "Thank YOU!" to everyone below who has contributed to one or more Re